W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
在子類中,你想要擴(kuò)展定義在父類中的property的功能。
考慮如下的代碼,它定義了一個property:
class Person:
def __init__(self, name):
self.name = name
# Getter function
@property
def name(self):
return self._name
# Setter function
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
self._name = value
# Deleter function
@name.deleter
def name(self):
raise AttributeError("Can't delete attribute")
下面是一個示例類,它繼承自Person并擴(kuò)展了 name
屬性的功能:
class SubPerson(Person):
@property
def name(self):
print('Getting name')
return super().name
@name.setter
def name(self, value):
print('Setting name to', value)
super(SubPerson, SubPerson).name.__set__(self, value)
@name.deleter
def name(self):
print('Deleting name')
super(SubPerson, SubPerson).name.__delete__(self)
接下來使用這個新類:
>>> s = SubPerson('Guido')
Setting name to Guido
>>> s.name
Getting name
'Guido'
>>> s.name = 'Larry'
Setting name to Larry
>>> s.name = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "example.py", line 16, in name
raise TypeError('Expected a string')
TypeError: Expected a string
>>>
如果你僅僅只想擴(kuò)展property的某一個方法,那么可以像下面這樣寫:
class SubPerson(Person):
@Person.name.getter
def name(self):
print('Getting name')
return super().name
或者,你只想修改setter方法,就這么寫:
class SubPerson(Person):
@Person.name.setter
def name(self, value):
print('Setting name to', value)
super(SubPerson, SubPerson).name.__set__(self, value)
在子類中擴(kuò)展一個property可能會引起很多不易察覺的問題,因為一個property其實是 getter
、setter
和 deleter
方法的集合,而不是單個方法。因此,但你擴(kuò)展一個property的時候,你需要先確定你是否要重新定義所有的方法還是說只修改其中某一個。
在第一個例子中,所有的property方法都被重新定義。在每一個方法中,使用了 super()
來調(diào)用父類的實現(xiàn)。在 setter
函數(shù)中使用 super(SubPerson, SubPerson).name.__set__(self, value)
的語句是沒有錯的。為了委托給之前定義的setter方法,需要將控制權(quán)傳遞給之前定義的name屬性的 __set__()
方法。不過,獲取這個方法的唯一途徑是使用類變量而不是實例變量來訪問它。這也是為什么我們要使用 super(SubPerson, SubPerson)
的原因。
如果你只想重定義其中一個方法,那只使用 @property 本身是不夠的。比如,下面的代碼就無法工作:
class SubPerson(Person):
@property # Doesn't work
def name(self):
print('Getting name')
return super().name
如果你試著運行會發(fā)現(xiàn)setter函數(shù)整個消失了:
>>> s = SubPerson('Guido')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "example.py", line 5, in __init__
self.name = name
AttributeError: can't set attribute
>>>
你應(yīng)該像之前說過的那樣修改代碼:
class SubPerson(Person):
@Person.getter
def name(self):
print('Getting name')
return super().name
這么寫后,property之前已經(jīng)定義過的方法會被復(fù)制過來,而getter函數(shù)被替換。然后它就能按照期望的工作了:
>>> s = SubPerson('Guido')
>>> s.name
Getting name
'Guido'
>>> s.name = 'Larry'
>>> s.name
Getting name
'Larry'
>>> s.name = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "example.py", line 16, in name
raise TypeError('Expected a string')
TypeError: Expected a string
>>>
在這個特別的解決方案中,我們沒辦法使用更加通用的方式去替換硬編碼的 Person
類名。如果你不知道到底是哪個基類定義了property,那你只能通過重新定義所有property并使用 super()
來將控制權(quán)傳遞給前面的實現(xiàn)。
值的注意的是上面演示的第一種技術(shù)還可以被用來擴(kuò)展一個描述器(在8.9小節(jié)我們有專門的介紹)。比如:
# A descriptor
class String:
def __init__(self, name):
self.name = name
def __get__(self, instance, cls):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
instance.__dict__[self.name] = value
# A class with a descriptor
class Person:
name = String('name')
def __init__(self, name):
self.name = name
# Extending a descriptor with a property
class SubPerson(Person):
@property
def name(self):
print('Getting name')
return super().name
@name.setter
def name(self, value):
print('Setting name to', value)
super(SubPerson, SubPerson).name.__set__(self, value)
@name.deleter
def name(self):
print('Deleting name')
super(SubPerson, SubPerson).name.__delete__(self)
最后值的注意的是,讀到這里時,你應(yīng)該會發(fā)現(xiàn)子類化 setter
和 deleter
方法其實是很簡單的。這里演示的解決方案同樣適用,但是在 Python的issue頁面 報告的一個bug,或許會使得將來的Python版本中出現(xiàn)一個更加簡潔的方法。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: