封装 | 艾利克斯部落

首页 » 编程 » Python » Python学习 » 面向对象 » 正文

封装

封装不是单纯意义上的隐藏。

 

一、封装中如何隐藏属性:

属性前加 “__” 就行了,这种方式只在类定义阶段发生, 如: __x = 1 

class A:
    __x = 1  # 隐藏属性 __x

    def __foo(self):  # 隐藏属性 __foo
        print("from foo")

    def bar(self):
        self.__foo()  # 在类内部可以直接调用内部隐藏属性
        print("from bar")


a = A()
# print(a.__x)   # 在类外部直接调用要出错
# a.__foo() # 在类外部直接调用会出错
print(a._A__x)  # 正确输出:1
a._A__foo()  # 正确输出:from foo
a.bar()  # 正确输出:from foo  和 from bar

print(A.__dict__)  # 通过__dict__查看到__x已经变形为 _A__x , __foo变形为了 _A__foo

 

 

 

这种变形的特点:(在类定义阶段就变形)
1. 在类外部无法直接obj.__AttrName
2. 在类内部可以直接是使用obj.__AttrName
3. 子类无法覆盖父类 __ 开头的属性

 

针对第3点来验证一下:

class Foo:
    def __func(self):  # 变成了_Foo__func
        print("from foo")


class Bar(Foo):  # 继承了父类
    def __func(self):  # 变成了_Bar__func
        print("from bar")

 

验证只能在定义阶段变形:

class B:
    def __init__(self, name):
        self.__name = name


b = B("alex")
B.__x = 1  # 没有变形,说明这种方式只在类定义阶段发生,这里已经是定义之后了
print(B.__dict__)  # 可以看出没有变

b.__age = 18
print(b.__dict__)  # __age依然没有变形,输出:'_B__name': 'alex', '__age': 18}

 

在来看个例子:

class D:
    def foo(self):
        print("from D.foo")

    def bar(self):
        print("from D.bar")
        self.foo()  # 这里实际上调的E.foo


class E(D):
    def foo(self):
        print("E.foo")


e = E()
e.bar()  # 输出:from D.bar  和 E.foo

这个例子里面 bar里调的实际是E的foo,因为调用请求是由E的对象发起的,那如果我就要在bar中就是调用D的foo怎么办呢?

给方法前加上__ ,由于在定义阶段加上__就会变形,所以D类的foo就变成了_D__foo了,E类里的foo就变成了_E__foo了

 

class D:
    def __foo(self):
        print("from D.foo")

    def bar(self):
        print("from D.bar")
        self.__foo()  # 加上__后就调用时就变成了 e._D__foo了,而e对象没有,去E找,E没有就去父类D找,在D找到了,所以输出 from D.foo


class E(D):
    def __foo(self):
        print("E.foo")


e = E()
e.bar()  # 输出:from D.bar  和 from D.foo

 

二、封装的意义

封装数据属性:明确的区分内外,控制外部对隐藏的属性的操作行为

# 封装数据属性:明确的区分内外,控制外部对隐藏的属性的操作行为
class People:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def tell_info(self):
        print("Name:<%s> Age:<%s>" % (self.__name, self.__age))

    def set_info(self, name, age):
        if not isinstance(name, str):
            print("名字必须是字符串")
            return
        if not isinstance(age, int):
            print("年龄必须是数字类型")
            return
        self.__name = name
        self.__age = age


p = People("alex", 18)
p.tell_info()  # 输出:Name:<alex> Age:<18>

p.set_info("Alex", 38)
p.tell_info()  # 输出:Name:<Alex> Age:<38>

 

封装方法:隔离复杂度

对于使用者而言无需关心内部如何实现,只需要调一个简单的接口就行了。

class ATM:
    def __card(self):
        print('插卡')

    def __auth(self):
        print('用户认证')

    def __input(self):
        print('输入取款金额')

    def __print_bill(self):
        print('打印账单')

    def __take_money(self):
        print('取款')

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()


a = ATM()
a.withdraw()

输出:

插卡
用户认证
输入取款金额
打印账单
取款

 

三、封装与可扩展性

class Room:
    def __init__(self, name, owner, weight, length):
        self.name = name
        self.owner = owner

        self.__weight = weight
        self.__lenght = length

    def tell_area(self):
        return self.__weight * self.__lenght


r = Room("客厅", "alex", 10, 10)
print(r.tell_area())

输出:100

 

class Room:
    def __init__(self, name, owner, weight, length, height):
        self.name = name
        self.owner = owner

        self.__weight = weight
        self.__lenght = length
        self.__height = height

    def tell_area(self):
        return self.__weight * self.__lenght * self.__height


r = Room("客厅", "alex", 10, 10, 10)
print(r.tell_area())

输出:1000

 

四、property

property是一个装饰器,使得方法可以像访问属性一样去访问。

 

BMI指数例子:

# 常规做法
class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    def bmi(self):
        return self.weight / (self.height ** 2)


p = People("alex", 50, 1.60)
print(p.bmi())  # 输出:19.531249999999996

 

# property改进
class People:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    @property
    def bmi(self):
        return self.weight / (self.height ** 2)


p = People("alex", 50, 1.60)
print(p.bmi)  # 输出:19.531249999999996

p.height = 1.75
print(p.bmi)  # 输出:16.3265306122449


# p.bmi = 2223333  # 报错,不能赋值,因为bmi其实并不是属性,而是一个方法,只不过是通过property将方法做的像属性了

 

另一个例子:

# 传统做法
class People:
    def __init__(self, name):
        self.__name = name

    def get_name(self):
        return self.__name


p = People("alex")
print(p.get_name())

 

# 加property使之可以用p.name 访问
class People:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name


p = People("alex")
print(p.name)

 

# 修改、删除属性
class People:
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    @name.setter  # 必须要首先有 name属性被property装饰
    def name(self, name):
        if not isinstance(name, str):
            print("名字必须是字符串类型")
            return
        self.__name = name

    @name.deleter  # 必须要首先有 name属性被property装饰
    def name(self):
        print("不允许删除")


p = People("alex")
print(p.name)  # 输出:alex

# 修改
p.name = "ALEX"  # 给p.name 赋值
print(p.name)  # 输出:ALEX ,修改成功

# 删除
del p.name  # 输出:不允许删除

 

 

 

赞 (0)

发表评论

*