简体   繁体   English

python:__getattr__的合作超级调用

[英]python: cooperative supercall of __getattr__

I'm working with somethign similar to this code: 我正在使用与此代码类似的somethign:

class BaseClass(object):
    def __getattr__(self, attr):
        return lambda:'1'

class SubClass(BaseClass):
    def foo(self):
        suffix = '2'
        return super(SubClass, self).foo() + suffix

class SubClass2(SubClass):
    def foo(self):
        suffix = '3'
        return super(SubClass2, self).foo() + suffix

o = SubClass2()
print o.foo()

I'd expect to see output of '123', but I instead I get an error AttributeError: 'super' object has no attribute 'foo' . 我希望看到'123'的输出,但我得到一个错误AttributeError: 'super' object has no attribute 'foo' Python isn't even attempting to use the base class's __getattr__ . Python甚至没有尝试使用基类的__getattr__

Without modifying the base class, and keeping the two super calls similar, I'm not able to get the output I want. 如果不修改基类,并保持两个超级调用相似,我就无法获得我想要的输出。 Is there any cooperative supercall pattern that will work for me here? 有没有合适的超级电话模式在这里适合我?

I understand that super() overrides getattr in some way to do what it needs to do, but I'm asking if there's any reasonable workaround that allows a subclass's __getattr__ to be called when appropriate. 我知道super()以某种方式覆盖getattr以完成它需要做的事情,但我问是否有任何合理的解决方法允许在适当的时候调用子类的__getattr__

Ah, this is a great question! 啊,这是一个很好的问题!

In short, what's going on here is that the CPython internals occasionally take shortcuts when they are doing attribute lookups, and this kind of surprising behaviour is one of the consequences (another one being improved performance). 简而言之,这里发生的事情是CPython内部在进行属性查找时偶尔会采用快捷方式,而这种令人惊讶的行为是其中一个后果(另一个是提高性能)。

To understand exactly what's happening in this situation, we need to venture into the definition of super : http://hg.python.org/cpython/file/c24941251473/Objects/typeobject.c#l6689 为了准确了解在这种情况下发生了什么,我们需要冒险进入super的定义: http//hg.python.org/cpython/file/c24941251473/Objects/typeobject.c#l6689

Notice specifically that it doesn't define tp_getattr (aka __getattr__ ), but does define tp_getattro (aka __getattribute__ ): 特别注意它没有定义tp_getattr (又名__getattr__ ),但确实定义了tp_getattro (又名__getattribute__ ):

PyTypeObject PySuper_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "super",                                    /* tp_name */
    ...
    0,                                          /* tp_getattr */
    ...
    super_getattro,                             /* tp_getattro */
    ...
};

(recall that __getattribute__ is called every time an attribute is requested, as opposed to __getattr__ , which is only called if the attribute doesn't exist on the object (roughly: if the attribute isn't in the object's __dict__ )). (回想一下, 每次请求一个属性时都会调用__getattribute__ ,而不是__getattr__ ,只有在对象上不存在该属性时才会调用它(粗略地说:如果该属性不在对象的__dict__ ))。

Next, looking into the definition of super_getattro (aka super.__getattribute__ ), we can see the implementation is approximately : 接下来,查看super_getattro (又名super.__getattribute__ )的定义,我们可以看到实现大致如下:

class super(object):
    def __init__(self, obj_type, obj):
        self.obj_type = obj_type
        self.obj = obj

    def __getattribute__(self, attr):
        i = self.obj_type.__mro__.find(self.obj_type)
        i += 1
        while i < len(obj_type.__mro__):
            cur_type = self.obj_type.__mro__[i]
            cur_dict = cur_type.__dict___
            res = cur_dict.get(attr)
            if res is not None:
                return res
            i += 1
        return object.__getattribute__(self, attr)

Which makes it obvious why super doesn't play well with __getattr__super is only checking for attributes in the parent class' __dict__ ! 这显然为什么super不能很好地与__getattr__ - super只检查父类' __dict__中的属性!

Fun aside: it seems like pypy (as of 2.1.0) behaves the same way: 有趣的是:似乎pypy (从2.1.0开始)的行为方式相同:

$ pypy super.py 
Traceback (most recent call last):
  File "app_main.py", line 72, in run_toplevel
  File "super.py", line 16, in <module>
    print o.foo()
  File "super.py", line 13, in foo
    return super(SubClass2, self).foo() + suffix
  File "super.py", line 8, in foo
    return super(SubClass, self).foo() + suffix
AttributeError: 'super' object has no attribute 'foo'

This seems to work correctly. 这似乎工作正常。 I don't currently see why the standard super class doesn't do this. 我目前没有看到为什么标准超类不会这样做。

class super2(super):
    def __getattr__(self, attr):
        return self.__self__.__getattr__(attr)

class BaseClass(object):
    def __getattr__(self, attr):
        return lambda:'1'

class SubClass(BaseClass):
    def foo(self):
        suffix = '2'
        return super2(SubClass, self).foo() + suffix

class SubClass2(SubClass):
    def foo(self):
        suffix = '3'
        return super2(SubClass2, self).foo() + suffix

o = SubClass2()
print o.foo()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM