3.1.6 封装
1. 封装其实就是在类定义的时候,对以__开头(不包括__xxx__是内置方法)的属性进行变形,并且只变形一次,只是类定义时。
2. 变形后,类外部无法直接使用obj_name.__attribute_name。但python无法做到真正的隐藏,外部依然可以使用变形后的属性进行调用。如:对象._类名__属性名
3. 类内部是可以直接调用__属性名。因为定义的时候都已经变形,内部使用时,直接使用变形后的属性。
4. 子类无法覆盖父类的__开头的属性。因为定义时就已经变形了。他们的类名不一样,变形后的属性也不一样。
# 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')
#
# def bar(self):
# self.__foo() #self._A__foo()
# print('from bar')
# print(A.__dict__)
# print(A.__x)
# print(A.__foo)
# a=A('egon')
# a._A__foo()
# a._A__x
# print(a.__name) #a.__dict__['__name']
# print(a.__dict__)
# a.bar()
'''
这种变形的特点:
1、在类外部无法直接obj.__AttrName
2、在类内部是可以直接使用:obj.__AttrName
3、子类无法覆盖父类__开头的属性
'''
需要注意的问题:
1. 没有真正限制我们从外部调用,可以使用变形后的属性。
2.变形的过程,只在类的定义时发生一次,在定义后的赋值操作,并不会变形。
3. 在继承中,父类如果不想让子类覆盖自己的属性,可以将方法定义为私有的。
# class Foo:
# def __func(self): #_Foo__func
# print('from foo')
#
#
# class Bar(Foo):
# def __func(self): #_Bar__func
# print('from bar')
# b=Bar()
# b.func()
# class B:
# __x=1
#
# def __init__(self,name):
# self.__name=name #self._B__name=name
#验证问题一:
没有真正限制我们从外部调用,可以使用变形后的属性。
print(B._B__x)
#是可以调用的。
#验证问题二:
变形的过程,只在类的定义时发生一次,在定义后的赋值操作,并不会变形。
B.__y=2
print(B.__dict__) # __y 不会变形。
b=B('egon')
print(b.__dict__)
b.__age=18
print(b.__dict__)
print(b.__age)
#验证问题三:
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为__开头。
class A: def foo(self): print('A.foo') def bar(self): print('A.bar') self.foo() # b.foo() 从对象本身从头开始找,需要跟super区别开来。super()沿着MRO列表,是基于调用对象的MRO往后找。而不是调用对象本身再开始找。 class B(A): def foo(self): print('B.foo') b = B() print(B.mro()) b.bar()
输出:A.bar
B.foo
class A: def __foo(self): #_A__foo print('A.foo') def bar(self): print('A.bar') self.__foo() #self._A__foo() class B(A): def __foo(self): #_B__foo print('B.foo') b = B() b.bar()
输出:A.bar
A.foo
#因为子类B没有_A__foo(),所以输出的是父类自己的__foo(),故父类定义的__方法并不会被子类覆盖。
浙公网安备 33010602011771号