一、引子
从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小猫,小狗,小王八,还有alex一起装进麻袋,然后把麻袋封上口子。照这种逻辑看,封装=‘隐藏’,这种理解是相当片面的
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
1、代码
class A: __x=1 #_A__x=1 def __init__(self,name): self.__name=name #self._A__name=name def __foo(self): #def _A__foo(self): print('run foo') print(A.__dict__)
二、自动变形的特点
1、这仅仅这是一种变形操作
1、代码
print(A.__dict__)
2、打印输出
{'__module__': '__main__', '_A__x': 1, '__dict__': <attribute '__dict__' of 'A' objects>, '__init__': <function A.__init__ at 0x0000000000B92378>, '__doc__': None, '_A__foo': <function A.__foo at 0x0000000000B92400>, '__weakref__': <attribute '__weakref__' of 'A' objects>}
类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式
2、在类内部是可以直接使用:obj.__AttrName
1、代码
a=A('egon') print(a.__dict__)
2、打印结果
{'_A__name': 'egon'}
类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
3、在类外部无法直接obj.__AttrName
1、打印类的属性代码
1、代码
print(A.__x)
2、打印输出
Traceback (most recent call last): File "F:/s13/day07/17 如何实现属性的隐藏.py", line 11, in <module> print(A.__x) AttributeError: type object 'A' has no attribute '__x'
2、打印对象的属性
1、代码
print(A.__foo)
2、打印输出结果
Traceback (most recent call last): File "F:/s13/day07/17 如何实现属性的隐藏.py", line 12, in <module> print(A.__foo) AttributeError: type object 'A' has no attribute '__foo'
这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的
4、子类无法覆盖父类__开头的属性
1、代码
def bar(self): self.__foo() #self._A__foo() print('from bar') a=A('egon') a.bar()
2、输出结果
run foo from bar
在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的
1、代码
a=A('egon') a._A__foo() a._A__x #print(a.__name) #a.__dict__['__name'] print(a.__dict__)
2、打印输出结果
run foo {'_A__name': 'egon'}
5、最终总结注释
#其实这仅仅这是一种变形操作 #类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式: class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到. #A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
三、变形需要注意的问题
1、外部直接访问属性
1、代码
class Foo: def __func(self): #_Foo__func print('from foo') class Bar(Foo): def __func(self): #_Bar__func print('from bar') class B: __x=1 def __init__(self,name): self.__name=name #self._B__name=name print(B._B__x)
2、打印输出
1
这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2、变形的过程只在类的定义时发生一次
1、代码
B.__y=2 print(B.__dict__) b=B('egon') print(b.__dict__) b.__age=18 print(b.__dict__) print(b.__age)
2、打印输出
{'__module__': '__main__', '__y': 2, '__doc__': None, '_B__x': 1, '__dict__': <attribute '__dict__' of 'B' objects>, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__init__': <function B.__init__ at 0x0000000000A62488>} {'_B__name': 'egon'} {'_B__name': 'egon', '__age': 18} 18
变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
3、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
1、代码
class A: def foo(self): print('A.foo') def bar(self): print('A.bar') self.foo() #b.foo() class B(A): def foo(self): print('B.foo') b=B() b.bar()
2、打印输出
A.bar B.foo
3、带注释的
class A: def __foo(self): #在定义时就变形为_A__foo print('A.foo') def bar(self): print('A.bar') self.__foo() #只会与自己所在的类为准,即调用_A__foo class B(A): def __foo(self): #_B__foo print('B.foo')