6.2属性管理
1.属性管理的背后
要理解属性覆盖,就要先理解python的__dict__属性。当我们调用对象的属性时,这个属性可能有很多来源。除了来自对象属性和类属性,这个属性还可能是从祖先那里继承来的,被记录在__dict__中。这个__dict__是一个词典,键为属性名,对应的值为某个属性。Python在寻找对象的属性时,会按照继承关系一次寻找__dict__。
👇类和对象,Chicken类继承自Bird类,而summer为Chicken类的一个对象:
class Bird(object):
feather = True
def chirp(self):
print("some sound")
class Chicken(Bird):
fly = False
def __init__(self,age):
self.age = age
def chirp(self):
print("ji")
summer = Chicken(2)
print("===> summer")
print(summer.__dict__)
print("===>Chicken")
print(Chicken.__dict__)
print("===>Bird")
print(Bird.__dict__)
print("===>object")
print(object.__dict__)
___________________________
===> summer
{'age': 2}
===>Chicken
{'__module__': '__main__', 'fly': False, '__init__': <function Chicken.__init__ at 0x1029455e0>, 'chirp': <function Chicken.chirp at 0x102945670>, '__doc__': None}
===>Bird
{'__module__': '__main__', 'feather': True, 'chirp': <function Bird.chirp at 0x102945550>, '__dict__': <attribute '__dict__' of 'Bird' objects>, '__weakref__': <attribute '__weakref__' of 'Bird' objects>, '__doc__': None}
===>object
{'__repr__': <slot wrapper '__repr__' of 'object' objects>, '__hash__': <slot wrapper '__hash__' of 'object' objects>, '__str__': <slot wrapper '__str__' of 'object' objects>, '__getattribute__': <slot wrapper '__getattribute__' of 'object' objects>, '__setattr__': <slot wrapper '__setattr__' of 'object' objects>, '__delattr__': <slot wrapper '__delattr__' of 'object' objects>, '__lt__': <slot wrapper '__lt__' of 'object' objects>, '__le__': <slot wrapper '__le__' of 'object' objects>, '__eq__': <slot wrapper '__eq__' of 'object' objects>, '__ne__': <slot wrapper '__ne__' of 'object' objects>, '__gt__': <slot wrapper '__gt__' of 'object' objects>, '__ge__': <slot wrapper '__ge__' of 'object' objects>, '__init__': <slot wrapper '__init__' of 'object' objects>, '__new__': <built-in method __new__ of type object at 0x1026e26c8>, '__reduce_ex__': <method '__reduce_ex__' of 'object' objects>, '__reduce__': <method '__reduce__' of 'object' objects>, '__subclasshook__': <method '__subclasshook__' of 'object' objects>, '__init_subclass__': <method '__init_subclass__' of 'object' objects>, '__format__': <method '__format__' of 'object' objects>, '__sizeof__': <method '__sizeof__' of 'object' objects>, '__dir__': <method '__dir__' of 'object' objects>, '__class__': <attribute '__class__' of 'object' objects>, '__doc__': 'The base class of the class hierarchy.\n\nWhen called, it accepts no arguments and returns a new featureless\ninstance that has no instance attributes and cannot be given any.\n'}
这个顺序是按照与summer对象的亲近关系排列的。第一部分为summer对象自身的属性,也就是age。第二部分为chicken类的属性,比如fly和__init__()方法。第三部分为Bird类的属性,比如feather。最后一部分属于object类,有诸如___doc___之类的属性。
用内置函数来查看对象summer的属性,可以发现它包含了全部四个部分,分别存在summer/chicken/bird/object这四层。对象不会重复存储其祖先类的属性。
某个属性可能在不同层被重新定义。python在向下遍历的过程中,会选取先遇到的那一个。这正是属性覆盖的原理所在。在上面的输出中,chicken和bird都有chirp()方法。如果从summer调用chirp()方法,那么使用的将是对象summer关系更近的chicken的版本:
summer.chirp()
______________
ji
子类的属性比父类的同名属性有优先权,这也是属性覆盖的关键。
⬆️是调用属性的操作,如果进行赋值,那么python就不会分层深入查找了。
👇创建一个新的Chicken类的对象aumtumn,并通过autmn修改feather这一类属性:
autumn = Chicken(3)
autumn.feather = False
print(summer.feather)
____________________
True
尽管autumn修改了feather属性值,但它并没有影响到Bird到类属性。使用下面的方法查看autumn的对象属性时,会发现新建了一个名为feather的对象属性。
print(autumn.__dict__)
______________________
{'age': 3, 'feather': False}
因此,python在为属性赋值时,只会搜索对象本身的__dict__。如果找不到对应属性,则将在__dict__中增加。在类定义的方法中,如果用self引用对象,则也会遵守相同的规则。
也可以不依赖继承关系,直接去操作某个祖先类的属性:
Bird.feather = 3
其等效于修改的Bird的__dict__:
Bird.__dict__["feather"] =3