接下来是我的代码:
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')
Run Code Online (Sandbox Code Playgroud)
我知道__getattr__必须是 2 个参数,但如果该属性不存在,我想获得一个默认属性。
我怎样才能得到它,谢谢
和
我发现如果定义了__setattr__,我的下一个代码也无法运行
class foo:
def __init__(self):
self.a={}
def __setattr__(self,name,value):
self.a[name]=value
a=foo()#error ,why
Run Code Online (Sandbox Code Playgroud)
嗨亚历克斯,我改变了你的例子:
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')
Run Code Online (Sandbox Code Playgroud)
它打印 {'a': 'boh'},而不是 'boh' 我认为它会打印 self.a 而不是 self.a['a'],这显然是不想看到的
为什么,有什么方法可以避免它
您的第一个问题:您正在定义一个旧式类(我们知道您使用的是 Python 2.something,即使您没有告诉我们,因为您正在使用print作为关键字;-)。在Python 2中:
class foo:
Run Code Online (Sandbox Code Playgroud)
意味着您正在定义一个旧式(又名遗产)类,其行为有时可能相当古怪。永远不要这样做——没有充分的理由!旧式类的存在只是为了与依赖于其怪癖的旧遗留代码兼容(最终在 Python 3 中被废除)。使用新样式类代替:
class foo(object):
Run Code Online (Sandbox Code Playgroud)
然后检查if x in self:不会引起递归__getattr__调用。然而,无论如何它都会导致失败,因为您的类没有定义__contains__方法,因此您无法检查是否x包含在该类的实例中。
如果您想要做的是是否在 的实例字典中x定义,请不要打扰:在这种情况下甚至不会被调用 - 仅当在 中找不到该属性时才会调用它。self__getattr__self
getattr为了支持对内置函数的三参数调用,如有必要,只需raise AttributeError在您的__getattr__方法中进行调用(就像根本没有__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')
Run Code Online (Sandbox Code Playgroud)
这将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')
Run Code Online (Sandbox Code Playgroud)
这也打印sss。代替
self.__dict__['blah'] = {}
Run Code Online (Sandbox Code Playgroud)
你也可以使用
object.__setattr__(self, 'blah', {})
Run Code Online (Sandbox Code Playgroud)
这种“对超类实现的向上调用”(您也可以通过super内置函数获得)是“不要直接调用特殊方法,调用内置函数或使用运算符”规则的罕见例外之一 - - 在这里,您想要专门绕过正常行为,因此显式特殊方法调用是可能的。