Python如何在子類裡擴充套件父類的property?
阿新 • • 發佈:2018-12-29
《python cookbook》8.8節討論子類擴充套件property時,一開始都暈了,思考了半天才勉強弄懂一點,趕快記下來。廢話不多說,先上程式碼:
class Person: def __init__(self, name): self.name = name @property def name(self): print("I am in the Person's name getter") return self._name @name.setter def name(self, value): print("I am in the Person's name setter") if not isinstance(value, str): raise TypeError('Expected a string') self._name = value class SubPerson(Person): @property def name(self): print("I am in the SubPerson's name getter") super().name @name.setter def name(self, value): print("I am in the SubPerson's name setter") super(SubPerson, SubPerson).name.__set__(self, value)
我知道property其實就是特殊的描述符,但是為啥在setter裡面必須顯式呼叫父類name的__set__函式呢?直接super().name = value難道不能觸發__set__函式嗎?試試看:
class SubPerson(Person): @property def name(self): print("I am in the SubPerson's name getter") super().name @name.setter def name(self, value): print("I am in the SubPerson's name setter") super().name = value >>> sp = SubPerson('shy') I am in the SubPerson's name setter Traceback (most recent call last): File "<pyshell#25>", line 1, in <module> sp = SubPerson('shy') File "<pyshell#11>", line 3, in __init__ self.name = name File "<pyshell#24>", line 9, in name super().name = value AttributeError: 'super' object has no attribute 'name'
果然報錯,提示super物件沒有name屬性,WTF!為什麼可以get但是不能set?一直沒有查到答案,最後help(super),才發現蛛絲馬跡:
>>> help(super) Help on class super in module builtins: class super(object) | super() -> same as super(__class__, <first argument>) | super(type) -> unbound super object | super(type, obj) -> bound super object; requires isinstance(obj, type) | super(type, type2) -> bound super object; requires issubclass(type2, type) | Typical use to call a cooperative superclass method: | class C(B): | def meth(self, arg): | super().meth(arg) | This works for class methods too: | class C(B): | @classmethod | def cmeth(cls, arg): | super().cmeth(arg) | | Methods defined here: | | __get__(self, instance, owner, /) | Return an attribute of instance, which is of type owner. | | __getattribute__(self, name, /) | Return getattr(self, name). | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | __new__(*args, **kwargs) from builtins.type | Create and return a new object. See help(type) for accurate signature. | | __repr__(self, /) | Return repr(self). | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __self__ | the instance invoking super(); may be None | | __self_class__ | the type of the instance invoking super(); may be None | | __thisclass__ | the class invoking super()
super本身只有__getattribute__,沒有__setattr__,只對獲取屬性做了代理。因此設定的時候,會直接設定super()物件本身的屬性,所以出現如上的錯誤提示,因此只能夠顯式呼叫name的__set__方法。。。。
另外一個坑就是如果子類全面擴充套件父類的property,可以用上面的方法,但是如果只是擴充套件get或者set方法,就不行了,如下:
>>> class SubPerson(Person):
@property
def name(self):
print("I am in SubPerson's getter")
super().name
>>> sp = SubPerson('shy')
Traceback (most recent call last):
File "<pyshell#48>", line 1, in <module>
sp = SubPerson('shy')
File "<pyshell#11>", line 3, in __init__
self.name = name
AttributeError: can't set attribute
父類的setter方法消失了,這裡比較好理解,property是描述符,是get,set,delete的集合,子類僅僅只設定了get,set和delete相當於根本沒有設定。如果想要繼承父類的property,只能顯式的用父類的property來裝飾,如下:
>>> class SubPerson(Person):
@Person.name.getter
def name(self):
print("I am in SubPerson's getter")
return super().name
>>> sp = SubPerson('shy')
I am in the Person's name setter
>>> sp.name
I am in SubPerson's getter
I am in the Person's name getter
'shy'
此時返回的name特性,其實是複製了Person.name描述符所有方法的一個新的描述符。。
擴充套件子類的property,需要對描述符和super的機制有比較深入的瞭解,現在只是模模糊糊弄了個半懂,mark在此,隨時修改。