【问题标题】:why is there difference between calling super().foo and super().__getattribute__("foo")为什么调用 super().foo 和 super().__getattribute__("foo") 有区别
【发布时间】:2021-05-04 05:26:46
【问题描述】:

我认为 super() 总是会尝试使用基类中的方法(或属性)运行,直到我遇到一个使用super().__getattribute__("foo") 获取子类属性的用例。简化代码如下所示。

class Base:
    value = "base"

    def foo(self):
        print(f"Base foo, {self.value}")

class Derived(Base):
    value = "derived"

    def foo(self):
        print(f"Derived foo, {self.value}")


    def bar(self):
        print(f"Derived bar {self.value}")
        print(super().value)
        print(super().__getattribute__("value"))
        super().foo()
        super().__getattribute__('foo')()

d = Derived()
d.bar()

输出,

Derived bar derived
base
derived
Base foo, derived
Derived foo, derived

有点超出我之前的理解,只有__getattribute__是个例外吗?这个doc我无法获得更多细节,希望有人能帮助我更清楚地理解这一点,谢谢!


编辑关注__getattribute__问题,如下:

class Base:
    value = "base"

    def foo(self):
        print(f"Base foo, {self.value}")


    def __getattribute__(self, k):
        print(f"Base get attr {self}, {k}")
        return super().__getattribute__(k)


class Derived(Base):
    value = "derived"

    def foo(self):
        print(f"Derived foo, {self.value}")


    def __getattribute__(self, k):
        print(f"Derived get attr {self}, {k}")
        return super().__getattribute__(k)


    def bar(self):
        print("Derived bar")
        print(super().value)
        print(super().__getattribute__("value"))

d = Derived()
d.bar()

输出是:

Derived get attr <__main__.Derived object at 0x7fb0621dba90>, bar
Base get attr <__main__.Derived object at 0x7fb0621dba90>, bar
Derived bar
base
Base get attr <__main__.Derived object at 0x7fb0621dba90>, value
derived

【问题讨论】:

  • 总是获取基类中的属性/方法(而是方法解析顺序中的下一个类)。你认为super 哪里没有这样做,究竟是什么?
  • 也许,这将揭示:object.__getattribute__(d, 'value')
  • 最近有人发了一个非常相似的问题:stackoverflow.com/questions/67377122/…
  • @juanpa.arrivillaga:我一直以为super()实际上返回了一个基类的object。很长一段时间我才明白,它只是为属性查找一次的代理。
  • @SergeBallesta 你从哪里得到这个it was only a proxy to look once for an attribute?我认为让我感到困惑的是为什么super().xxx 不调用object.__getattribute__(d, 'xxx')

标签: python python-3.x super


【解决方案1】:

super() 帮助器创建一个代理,该代理修改自身的属性访问。因此:

  • super().foosuper 代理上查找 foo
  • super().__getattribute__super 代理上查找 __getattribute__

值得注意的是,任何一方的结果都不知道它是通过super 查找的:对结果的任何进一步操作都会正常进行。当查找的事物是数据描述符(如方法或属性)时,它绑定到初始的self,而不是super

最终,查找super().__getattribute__ 在通常情况下只会直接或通过包装器找到标准object.__getattribute__ 并将其绑定到self。因此,调用super().__getattribute__("foo") 等同于object.__getattribute__(self, "foo") - 没有super 参与查找.foo


比较表达式super().foosuper().__getattribute__("foo") 的分解方式可能会有所帮助:

# super().foo
s = super()
foo = s.foo
# super().__getattribute__("foo")
s = super()
g = s.__getattribute__
foo = g("foo")

在第二种情况下,super 代理不参与查找.foo

【讨论】:

  • 谢谢,你的回答真的让我明白了,我从来没有意识到 s.__getattribute__s.__getattribute('foo') 在这种情况下是两个步骤(只是忘记了 function 也是一个可调用对象,当结合此super() 调用)。
【解决方案2】:

更具体地说,super 在查找某些属性/方法时实际上会跳过当前类。在单继承的情况下,这相当于在基类中查找。这总是在上面发生。

In [38]: class Base:
    ...:     value = "base"
    ...:
    ...:     def foo(self):
    ...:         print(f"Base foo, {self.value}")
    ...:
    ...: class Derived(Base):
    ...:     value = "derived"
    ...:
    ...:     def foo(self):
    ...:         print(f"Derived foo, {self.value}")
    ...:
    ...:
    ...:     def bar(self):
    ...:         print(f"Derived bar {self.value}")
    ...:         print(super().value)
    ...:         print(super().__getattribute__("value"))
    ...:         super().foo()
    ...:         super().__getattribute__('foo')()
    ...:
    ...: d = Derived()

In [39]: Derived.mro()
Out[39]: [__main__.Derived, __main__.Base, object]

我怀疑让你感到困惑的是:

super().__getattribute__("value")

好吧,在这种情况下,super() 跳过__main__.Derived,查找__main__.Base,没有找到任何东西,然后最后,在object 中找到__getattribute__。然后它不会神奇地使所有其他属性访问跳过Derived。确实,object.__getattribute__ 会发生反正,注意:

In [40]: object.__getattribute__(d, 'value')
Out[40]: 'derived'

因此,当您将 "foo" 传递给 object.__getattribute__ 时,这并没有什么不同,

In [41]: object.__getattribute__(d, 'foo')
Out[41]: <bound method Derived.foo of <__main__.Derived object at 0x7f927ba46460>>

现在,当您调用super().foo 时,它跳过 __main__.Derived,查找__main__.Base 并找到foo,因此本质上是:

In [42]: Base.foo(d)
Base foo, derived

【讨论】:

  • 谢谢,我对super().__getattribute__("value")还是有点迷糊,你说doesn't find anything, then finally, finds __getattribute__ in object的时候,Base里面不是有value属性吗,怎么找不到?我在功能bar中尝试了另一种方式,当我使用super().bar时,它会以AttributeError: 'super' object has no attribute 'bar'失败,为什么不回退到object.__getattribute__(d, 'bar')
  • @kuixiong value 无关紧要,super() 正在解析__getattribute__,而不是valuesuper().__getattribute__ is object.__attribute__.
  • 谢谢,我想现在我明白了表面原因,super().__getattribute__ 只等于object.__getattribute__,除非我在Base 中明确实现它。但是你知道为什么super().value 不像d.value 那样调用__getattribute__ 吗?
  • @kuixiong 它确实调用了 super__getattribute__,因为 super().valuesuper 对象上调用 .value
  • 为什么?我认为每次访问实例的属性时都会调用__getattribute__,如here 所述
猜你喜欢
  • 2010-12-12
  • 1970-01-01
  • 2019-04-26
  • 2015-03-08
  • 2018-07-01
  • 2017-06-23
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多