为什么getattr()的行为和我想的不一样?我认为这段代码应该输出'sss
这是我的代码:
class foo:
def __init__(self):
self.a = "a"
def __getattr__(self,x,defalut):
if x in self:
return x
else:return defalut
a=foo()
print getattr(a,'b','sss')
我知道 __getattr__
这个方法必须有两个参数,但我想在属性不存在的时候获取一个默认的属性。
我该怎么做呢,谢谢。
还有,
我发现如果定义了 __setattr__
,我的下一段代码也无法运行。
class foo:
def __init__(self):
self.a={}
def __setattr__(self,name,value):
self.a[name]=value
a=foo()#error ,why
嗨,亚历克斯,
我修改了你的例子:
class foo(object):
def __init__(self):
self.a = {'a': 'boh'}
def __getattr__(self, x):
if x in self.a:
return self.a[x]
raise AttributeError
a=foo()
print getattr(a,'a','sss')
它打印的是 {'a': 'boh'},而不是 'boh'。
我认为它应该打印 self.a 而不是 self.a['a'],这显然不是我想要的结果。
为什么会这样?有没有办法避免这种情况?
2 个回答
你把 getattr
这个内置函数和 __getattr__
这个方法搞混了。getattr
是用来动态获取对象某个属性的,按名字来找,发生在程序运行的时候。而 __getattr__
是当你访问一个对象里不存在的属性时会被调用的方法。
你不能在 __getattr__
里面使用
if x in self:
,因为这样会导致 in
操作符调用 __getattr__
,这会造成无限循环。
如果你只是想让所有未定义的属性都返回一个默认值,那么
def __getattr__(self, ignored):
return "Bob Dobbs"
你的第一个问题是:你在定义一个旧式类(我们知道你在用Python 2.x,因为你用print
作为关键字;-)。在Python 2中:
class foo:
这意味着你在定义一个旧式类,也叫遗留类,它的行为有时候会很奇怪。千万不要这样做——没有任何好理由!旧式类只是为了兼容那些依赖它们奇怪行为的老旧代码而存在(在Python 3中已经被淘汰)。应该使用新式类:
class foo(object):
这样的话,检查if x in self:
就不会导致递归调用__getattr__
。不过,它还是会失败,因为你的类没有定义__contains__
方法,所以你无法检查x
是否在这个类的实例中。
如果你想知道x
是否在实例字典中,不用费心:在这种情况下__getattr__
根本不会被调用——它只在属性没有在self
中找到时才会被调用。
如果你想支持三参数调用内置的getattr
,只需在你的__getattr__
方法中根据需要raise AttributeError
(就像你根本没有__getattr__
方法时会发生的那样),内置函数会正常工作(这是内置函数的职责,拦截这种情况并返回提供的默认值)。这就是为什么我们从不直接调用特殊方法,比如__getattr__
,而是使用内置函数和运算符,它们会在内部调用这些特殊方法——内置函数和运算符提供了额外的价值。
举个例子,比较容易理解:
class foo(object):
def __init__(self):
self.blah = {'a': 'boh'}
def __getattr__(self, x):
if x in self.blah:
return self.blah[x]
raise AttributeError
a=foo()
print getattr(a,'b','sss')
这会打印出sss
,正如你所期望的那样。
如果你添加一个__setattr__
方法,那么这个方法会拦截每一个对self
设置属性的尝试——包括self.blah =
任何东西。所以——当你需要绕过你自己定义的__setattr__
时——你必须使用不同的方法。例如:
class foo(object):
def __init__(self):
self.__dict__['blah'] = {}
def __setattr__(self, name, value):
self.blah[name] = value
def __getattr__(self, x):
if x in self.blah:
return self.blah[x]
raise AttributeError
a=foo()
print getattr(a,'b','sss')
这也会打印出sss
。而不是
self.__dict__['blah'] = {}
你也可以使用
object.__setattr__(self, 'blah', {})
这种“调用超类实现”的方式(你也可以通过super
内置函数获得)是“不要直接调用特殊方法,而是调用内置函数或使用运算符”的规则中的少数例外之一——在这里,你想特别绕过正常行为,所以显式调用特殊方法是一个可能的选择。