问题 'super'对象在python3中没有属性'__getattr__'


如何覆盖 __getattr__ 用python 3和继承?

当我使用以下内容时:

class MixinA:
    def __getattr__(self, item):
       # Process item and return value if known
       if item == 'a':
           return 'MixinA'

       # If it is unknown, pass it along to give 
       # a chance to another class to handle it
       return super().__getattr__(item)

class MixinB:
    def __getattr__(self, item):
       # Process item and return value if known
       if item == 'b':
           return 'MixinB'

       # If it is unknown, pass it along to give 
       # a chance to another class to handle it
       return super().__getattr__(item)

class Example(MixinA, MixinB):
    # main class
    pass

我收到这个错误。

>>> e  = Example()
>>> e.a
'MixinA'
>>> e.b
'MixinB'
>>> e.c
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
...
AttributeError: 'super' object has no attribute '__getattr__'  

难道我不能只引用属性错误引用原始类和属性吗?也就是说:

AttributeError: 'Example' object has no attribute 'c'

PS:我找到了这篇文章 “超级”对象没有调用__getattr__ 但我不确定是否有解决方案。


2689
2018-03-04 17:34


起源

这是使用的海报儿童用例 properties。 - dursk
@mattm你是什么意思?你能更明确吗?我不想使用属性。让我试着解释一下我想要实现的目标。我正在创建几个mixins,根据名称动态添加一些属性的额外选项。例如,如果一个attr以 _logs,然后你可以通过添加获得摘录 __excerpt =>例如:如果一个obj有一个 xxx_logs 属性, obj.xxx_logs__excerpt 会被拦截并且会起作用。 - Michael


答案:


如果你想用mixin风格的类做事,那么你需要创建一个始终引发一个base-attribute-getting-class AttributeError

class AttrGetter:
    def __getattr__(self, item):
        raise AttributeError(item)

class MixinA(AttrGetter):
    def __getattr__(self, item):
       if item == 'a':
           return 'MixinA'
       return super().__getattr__(item)

class MixinB(AttrGetter):
    def __getattr__(self, item):
       if item == 'b':
           return 'MixinB'
       return super().__getattr__(item)

class Example(MixinA, MixinB):
    pass

# mro stands for method resolution order.
# It defines the order in which classes are searched for attributes.
# We're looking for __getattr__ in this circumstance.
print(Example.mro())
# [<class '__main__.Example'>, <class '__main__.MixinA'>, <class '__main__.MixinB'>, 
#    <class '__main__.AttrGetter'>, <class 'object'>]
# As you can see, searches for __getattr__ only check AttrGetter after having checked 
# both mixins first.

e = Example()
print(e.a)
print(e.b)
print(e.c)

但是,属性允许您以更简单,更简洁的方式执行此操作。

class MixinA:
    @property
    def a(self):
        return "MixinA"

class MixinB:
    @property
    def b(self):
        return "MixinB"

class Example(MixinA, MixinB):
    pass

e = Example()
print(e.a)
print(e.b)
print(e.c)

4
2018-03-04 20:27





Python属性检索机制以类的方式工作 __getattr__ 被称为“最后一个资源”,试图获取该类实例的属性。

你的代码是对的 - 由于你的超级类“示例”而失败 - 在这种情况下是“对象” - 没有 __getattr__ 属性。 如果您不是深层次的层次结构,并且想要为直接从对象继承的类执行自定义属性检索(在Py3中可以表示为根本没有任何基础,就像在代码中那样) - 只需引发“AttributeError”如果您的自定义查找失败。

相反,如果你打算让自定义搜索优先于Python的普通属性retreival机制(而不是被称为后备),你应该实现 __getattribute__ 代替 __getattr__

在这种情况下,基类 - 对象 - 确实有一个 __getattribute__你需要调用普通属性检索的方法 - 问题是你必须调用它 一切 你想要的 - 包括方法名称和你设置的已知属性。即::

class Example:

    def __init__(self):
       self.attrs_to_override = ["attr1", "foo", "bar"]

    def docustomstuff(self, attr):
        ...

    def __getattribute__(self, attr):
       getter = super().__getattribute__
       if attr in getter("attrs_to_override"):
            getter("docustomstuff")(attr)
       return getter(attr)

简而言之,如果您认为应该实施 __getattribute__ 代替 __getattr__ 你可能正在尝试错误的方法,除非在非常具体的情况下。你可能在这里不知道的是: __getattr__ 如果所需名称的普通属性尚不存在,则不会被调用,因此不需要在超类中调用它(除非案例中的超类不是对象并且具有已知的自定义)

编辑

或者,只需检查下一个超类是否有 __getattr__ 以最简单的方式:

...
if hasattr(super(), "__getattr__"):
    return super().__getattr__(attr)
raise AttributeError

7
2018-03-04 18:23



感谢您的答复。是的我想我明白了它们之间的区别 __getattr__ 和 __getattribute__ 我的意思是使用 __getattr__。我不想提高 AttributeError 因为我想有几个mixin实现 __getattr__方法,我希望他们都有机会拦截它。我编辑了我的帖子来展示这个例子。让我知道你的想法。基本上我已经得到了 AttributeError 但是我希望它能在高级班级和财产上而不是在 super。任何想法? - Michael
在那里 - 我添加了一个如何使用它的示例 __getattr__ - jsbueno