![](/img/trans.png)
[英]Overriding __getattr__ and __setattr__ methods in a class with properties
[英]Class properties and __setattr__
在Python類中,當我使用__setattr__
它優先於在此類(或任何基類)中定義的屬性。 請考慮以下代碼:
class Test(object):
def get_x(self):
x = self._x
print "getting x: %s" % x
return x
def set_x(self, val):
print "setting x: %s" % val
self._x = val
x = property(get_x, set_x)
def __getattr__(self, a):
print "getting attr %s" % a
return -1
def __setattr__(self, a, v):
print "setting attr %s" % a
當我創建類並嘗試設置x
, __setattr__
而不是set_x
:
>>> test = Test()
>>> test.x = 2
setting attr x
>>> print test.x
getting attr x_
getting x: -1
-1
我想實現的是,在實際的代碼__setattr__
如果沒有相關的屬性,即只叫test.x = 2
應該叫set_x
。 我知道,我可以通過手動檢查是否容易達到這個a
是“X”是__setattr__
,然而,這將使一個糟糕的設計。 是否有更聰明的方法來確保__setattr__
為類和所有基類中定義的每個屬性的正確行為?
Python用於屬性的搜索順序如下:
__getattribute__
和__setattr__
property
__dict__
實例變量(設置屬性時,搜索結束於此處) __getattr__
由於__setattr__
是__setattr__
在第一位的,如果你有一個,你需要讓它變得聰明,除非希望它能處理你班級的所有屬性設置。 它可以通過以下兩種方式實現智能:使其僅處理特定的集合屬性,或使其處理除一些屬性集之外的所有屬性。 對於你不希望它處理的那些,調用super().__setattr__
。
對於您的示例類,處理“除'x'之外的所有屬性”可能是最簡單的:
def __setattr__(self, name, value):
if name == "x":
super(Test, self).__setattr__(name, value)
else:
print "setting attr %s" % name
這不是防彈的解決方案,但是,像你的建議,你可以檢查一個屬性被setattr
試圖訪問該編property
的對象,從類的屬性(使用getattr
類對象上)。
class Test(object):
def get_x(self):
x = self._x
print "getting x: %s" % x
return x
def set_x(self, val):
print "setting x: %s" % val
self._x = val
x = property(get_x, set_x)
@property # no fset
def y(self):
print "getting y: 99"
return 99
def __getattr__(self, a):
print "getting attr %s" % a
return -1
def __setattr__(self, a, v):
propobj = getattr(self.__class__, a, None)
if isinstance(propobj, property):
print "setting attr %s using property's fset" % a
if propobj.fset is None:
raise AttributeError("can't set attribute")
propobj.fset(self, v)
else:
print "setting attr %s" % a
super(Test, self).__setattr__(a, v)
test = Test()
test.x = 2
print test.x
#test.y = 88 # raises AttributeError: can't set attribute
print test.y
test.z = 3
print test.z
編輯:取代self.__dict__[a] = v
with super(Test, self).__setattr__(a, v)
,見@ Blckknght的回答
AFAIK,沒有干凈的方法來做到這一點。 這里的問題源於__getattr__
和__setattr__
之間的不對稱性。 只有當正常方式的屬性失敗時才調用前者,但后者被無條件地調用。 由於__setattr__
沒有通用的方法會失敗,我不知道是否有辦法可以改變這種行為。
最后,我認為獲得所需行為的唯一方法是將屬性的__setattr__
動作折疊到__setattr__
函數中 - 如果你這樣做,你也可以將getter的行為折疊成__getattr__
到從1個地方可以維護它。
我多次遇到這個問題,我的首選解決方案如下:
[property]
,定義get_[property]
和set_[property]
函數,但不使用裝飾器 __getattr__
和__setattr__
以便檢查是否存在這些函數,並在它們可用時使用它們。 這是一個最小的例子:
class SmartSetter(object):
def __init__(self,name):
self.set_name(name)
def get_name(self):
#return the name property
return self._name
def set_name(self,value):
#set the name property
self._name = value
def __getattr__(self,key):
#first condition is to avoid recursive calling of this function by
#__setattr__ when setting a previously undefined class attribute.
if not key.startswith('get_') and hasattr(self,'get_'+key):
return getattr(self,'get_'+key)()
#implement your __getattr__ magic here...
raise AttributeError("no such attribute: %s" % key)
def __setattr__(self,key,value):
try:
return getattr(self,'set_'+key)(value)
except AttributeError:
#implement your __setattr__ magic here...
return super(SmartSetter,self).__setattr__(key,value)
if __name__ == '__main__':
smart_setter = SmartSetter("Bob the Builder")
print smart_setter.name
#Will print "Bob the Builder"
smart_setter.name = "Spongebob Squarepants"
print smart_setter.name
#Will print "Spongebob Squarepants"
此方法的優點是它保留了除具有“getter”和“setter”方法的所有屬性的正常行為,並且在添加或刪除屬性時不需要對__getattr__
和__setattr__
函數進行任何修改。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.