在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__
中对于类和所有基类中定义的每个属性的正确行为?
答案 0 :(得分:24)
Python用于属性的搜索顺序如下:
__getattribute__
和__setattr__
property
__dict__
的实例变量(设置属性时,搜索结束于此处)__getattr__
由于__setattr__
排在第一位,如果您有一个,则需要将其设为智能,除非希望它处理您班级的所有属性设置。它可以通过以下两种方式实现智能:使其仅处理特定的集合属性,或使其处理除之外的所有属性集。对于您不希望它处理的那些,请致电super().__setattr__
。
对于您的示例类,处理“除'x'之外的所有属性”可能最简单:
def __setattr__(self, name, value):
if name == "x":
super(Test, self).__setattr__(name, value)
else:
print "setting attr %s" % name
答案 1 :(得分:6)
这不是一个防弹解决方案,但是,正如您的建议,您可以通过尝试从类的属性访问setattr
对象来检查属性是否property
(使用{ {1}}在类对象上。)
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
,如@ Blckknght的回答所示
答案 2 :(得分:2)
AFAIK,没有干净的方法来做到这一点。这里的问题来自__getattr__
和__setattr__
之间的asymmetry。只有当正常方式的属性失败时才调用前者,但后者被无条件地调用。由于__setattr__
没有通用的方法会失败,我不知道是否有办法可以改变这种行为。
最终,我认为获得所需行为的唯一方法是将属性的set_ action折叠到__setattr__
函数中 - 如果你这样做,你也可以折叠getter进入__getattr__
的行为,可以从1个地方维护它。
答案 3 :(得分:0)
我多次遇到这个问题,我的首选解决方案如下:
[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__
和{{1添加或删除属性时的函数。