一、引子
从封装本身的意思去理解,封装就好像是拿来一个麻袋,把小猫,小狗,小王八,还有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')