为什么 getattr() 没有像我想象的那样工作?我认为这段代码应该打印“sss”

zjm*_*126 2 python

接下来是我的代码:

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'],这显然是不想看到的

为什么,有什么方法可以避免它

Ale*_*lli 5

您的第一个问题:您正在定义一个旧式类(我们知道您使用的是 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内置函数获得)是“不要直接调用特殊方法,调用内置函数或使用运算符”规则的罕见例外之一 - - 在这里,您想要专门绕过正常行为,因此显式特殊方法调用是可能的。