【问题标题】:Super class appears to wrongly reference properties of derived class超类似乎错误地引用了派生类的属性
【发布时间】:2022-01-04 13:01:26
【问题描述】:

在下面的python代码中(我使用的是3.8),从A类派生的B类对象调用方法barfoo通过super()函数访问父类的成员。因此,我希望得到与直接在 A 上调用 barfoo 相同的结果。奇怪的是,返回的内容受 B 的 p 参数化的影响,这不应该发生,因为 A 应该屏蔽它的孩子,不应该吗?!这是要重现的代码:

class A(object):
    @property
    def p(self):
        return 3

    def bar(self):
        return self.p

    def foo(self):
        return self.bar()

class B(A):
    @property
    def p(self):
        return 6

    def bar(self):
        return super().p

    def foo(self):
        return super().bar()



a, b = A(), B()

print(a.p) # prints 3, OK
print(b.p) # prints 6, OK

print(a.bar()) # prints 3, OK
print(b.bar()) # prints 3, OK, since where accessing super().p 

print(a.foo()) # prints 3, OK
print(b.foo()) # prints 6, NOT OK, because we are accessing super().bar() and expect 3

我在这里疯了,所以如果有人能阐明这种行为的基本原理并提出一种避免它的方法,这将是最有帮助的。非常感谢。

【问题讨论】:

  • self 指的是实例,所以return self.p 实际上是在说return b.psuper() 只是获取 MRO 中的下一个类并将实例交给它的方法。它不会以任何方式更改实例。
  • 在您的用例中使用组合而不是继承可能会更好。
  • @JReichardt 你的印象是父类不能调用派生类的方法吗?
  • 作为提示:我的期望是从我从 C++ 中知道的,其中等效示例是 godbolt.org/z/4a6ao8Ybh - 我一直期望 thisself 表现类似。
  • @JReichardt:Python 是一种动态语言。您期望的 C++ 行为依赖于静态类型和静态分派。 Python 不是那样工作的。

标签: python python-3.x inheritance


【解决方案1】:

欢迎来到复杂的super

super() 这里是super(B, self) 的快捷方式。它返回一个代理,该代理将在 MRO 类中查找 B 之前的类,因此 A 和 super().bar() 将实际调用:

A.bar(self)

不改变原来的b对象...

A.bar(self) 实际上是... b.p 并且会给出6


如果你习惯了其他面向对象的语言,比如 C++,所有的一切都好像 Python 中的所有方法都是虚拟的(在 Java 措辞中不是最终的)

【讨论】:

  • 这实际上与super 没有太大关系,但事实上self 不会根据使用它的方法而改变类型。
  • 好的,谢谢。我相信评论说得很清楚。将print("A", self.__class__)print("B", self.__class__) 分别添加到类A 和B 的属性p 中,显示了self 的类和被调用函数的类有何不同。感谢您的帮助。
【解决方案2】:

super().attr 表示在父级中查找attr 属性。如果attr 是一个方法,它会查找该方法的代码(要执行的指令)。但这不会以任何方式修改传递的参数,它只是设置指令。

在 Python 中,self 是幕后的参数。如果c=C(),则c.meth(...) 表示C.meth(c, ...),即在C 类中定义的方法meth 的调用,第一个参数c(如果有的话,后面还有其他参数)。第一个 arg 成为方法实现中的 self 参数。 self 这个名字只是一个约定,不是特殊关键字)

回到问题。这是一个没有属性的简化程序,它的行为是一样的:

class A:
    P = 3 

    def bar(self):
        return self.P

    def foo(self):
        return self.bar()

class B(A):
    P = 6 

    def bar(self):
        return super().P

    def foo(self):
        return super().bar()

b.foo() 调用super().bar(),即父类A 中的bar()。该方法包含仅返回self.P 的代码。但是selfb,所以查找返回6。(在您的原始程序中p 是一个返回6 的属性)

【讨论】:

    猜你喜欢
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-05
    • 1970-01-01
    • 2016-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多