第七章 面向对象(8):封装

7.11 封装

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

首先封装怎么实现隐藏

属性的隐藏(数据属性,函数属性)

python类中,以__开头命名的属性会被隐藏。

注意:__开头__结尾的是python内置函数的意思,不是隐藏。

实际上,隐藏就是属性名的变形操作,在类定义的时候发生变形。

  • 隐藏属性的特点:
    1. 在类的外部无法用定义时的属性名来调用:obj__AttrName
    2. 在类的内部可以直接用定义时的属性名来调用: obj__AttrName

      在类定义的时候隐藏属性都发生了变化,所以也直接用变形后的属性名调用了

    3. 子类无法覆盖父类__开头的属性

class A:
    __x = 1  # 实际上隐藏是在定义类的时候变形了属性名,变成了 _A__x
    
    def __init__(self, name):
        self.__name = name  # 变形成 _A__name
        
    def __foo(self):  # 变形成 _A__foo
        print('run foo')
    
    def bar(self):
        self.__foo()  # 类调用的时候也被变形成 _A__foo了,所以跟上面变化后相同,所以在类内部可以调用
        print('from bar')

# print(A.__x)  # 会报错
# print(A.__foo)  # 会报错

a = A('a')
# print(a.__name)  # 会报错
print(a._A__name)  # 隐藏属性的特点1,不过不推荐这么访问

# 我们看看他们的名称空间发生了什么
print(A.__dict__)
print(a.__dict__)

a.bar()  # 隐藏属性的特点2

执行结果:

a
{'__module__': '__main__', '_A__x': 1, '__init__': <function A.__init__ at 0x0000000001E5A6A8>, 
'_A__foo': <function A.__foo at 0x0000000001E5A7B8>, '__dict__': <attribute '__dict__' of 'A' objects>, 
'__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
{'_A__name': 'a'}
run foo
from bar

我们可以看到,在定义类的时候,隐藏的属性发生了变形。

隐藏属性特点第3点代码例:

class Foo:
    def __func(self):  # _Foo__func
        print('from foo')
        
class Bar(Foo):
    def __func(self):  # _Bar__func
        print('from bar')

b = Bar()
print(Bar.__dict__)

总结这种变形需要注意的问题:

  1. 这种异常不是真正意义上的隐藏,只是变形。
  2. 这种变形只在类定义的时候就会发生,且只发生一次。类定义之后就全都变形了。

    在定义之后给类增加一个__开头的属性,定义的属性不会变形,而是以原名添加到类中。
    ```
    class B:
    __x = 1

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

    b = B('b')
    print(b.__dict__)
    B.__y = 2
    print(B.dict) # __y 会原样的增加到类的命名空间中

    b.__age = 11
    print(b.dict) # __age 会原样的增加到类的命名空间中
    执行结果:
    {'_B__name': 'b'}
    {'module': 'main', '_B__x': 1, 'init': <function B.__init__ at 0x000000000288A6A8>, 'dict': <attribute 'dict' of 'B' objects>, 'weakref': <attribute 'weakref' of 'B' objects>, 'doc': None, '__y': 2}
    {'_B__name': 'b', '__age': 11}
    ```
  3. 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为隐藏的
    ```
    class A:
    def __foo(self):
    print('A.foo')

     def bar(self):
         print('A.bar')
         self.__foo()

    class B(A):
    def __foo(self):
    print('B.foo')

    b = B()
    b.bar() # 如果foo不是隐藏属性,调用父类的bar里的foo会调用B类中的,而使用了隐藏属性就会调用A类的__foo
    执行结果:
    A.bar
    A.foo
    ```

封装的意义

1. 封装数据属性的意义:来明确区分内外,控制外部对隐藏属性的操作。

在类的外部不能直接访问封装属性,但是在类的内部还是可以直接访问。
从外部只能间接访问封装的属性。我们可以通过定义接口函数来控制访问的方法。
在外部不能直接修改类中的属性,只能通过接口改,

例:

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('年龄必须是int类型')
            return
        self.__name = name
        self.__age = age
        
p = People('a',11)

p.tell_info()
p.set_info('b', '12')
2. 封装方法属性的意义:隔离复杂度

类内部的细节处理跟使用者没有关系,外部使用者只要调用接口使用就可以了 即,内部处理的细节方法,就用封装的方法

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()

封装与可扩展性

我们有一个Room的类,开始没有height属性,后来追加。 因为用户调用的是接口方法,所以我们内部不论怎么修改,用户都是以相同的方式调用。 这就体现出了一种可扩展性

class Room:
    def __init__(self, name, owner, weight, lenth, height):  # 后增加了height
        self.name = name
        self.owner = owner
        
        self.__weight = weight
        self.__lenth = lenth
        self.__height = height  # 追加height属性
        
        
    def tell_result(self):  # 
        return self.__weight * self.__lenth * self.__height  # 追加了height

r = Room('卫生间','a', 10, 10, 10)  # 追加属性

# 用户调用接口是完全没有变化的
r.tell_result()

猜你喜欢

转载自www.cnblogs.com/py-xiaoqiang/p/11210450.html