【问题标题】:Access child's __class__ from a parent's method in Python从 Python 中的父方法访问子的 __class__
【发布时间】:2021-09-24 02:15:55
【问题描述】:

当我运行下面的代码时,最后我得到了对父类 A 的引用,而我希望引用子类 B

In [1]: class A:
    ...:     def f():
    ...:         print(__class__)
    ...: 

In [2]: class B(A): pass

In [3]: B.f()
<class '__main__.A'>

我知道我可以做类似的事情

In [4]: class A:
    ...:     def f(self):
    ...:         print(self.__class__)
    ...: 

In [5]: class B(A): pass

In [6]: B().f()
<class '__main__.B'>

但我想知道是否有什么方法可以去掉多余的括号。

【问题讨论】:

  • 您为什么希望B.f() 这样做?它只是在 A 的类块中定义的一个函数。
  • @wim:有趣的是,它不只是一个函数;不合格的__class__ 完全有效的事实是因为在Aclass 块中定义会改变f,而不是在Aclass 块中未定义的情况(它隐式地将其包装在一个伪造的闭包范围中,该范围提供__class__A)。 __class__ 是 OP 无法理解的魔法;我怀疑他们认为这是运行时魔术,而实际上(有点)定义时魔术(它更像是类定义时魔术而不是函数定义时魔术,但两者兼而有之)。
  • type(B.f) 是函数,type(B().f) 是方法。问题标题询问“父母的方法”,但问题主体不使用方法。一个在类块中没有定义参数并且没有用@staticmethod修饰的函数,首先是一件很奇怪的事情,所以我对OP在这里尝试做的事情有点茫然。 XY 问题?
  • @wim:当然,但是 Python 新手(甚至对 Python 不太熟悉的人)经常混合使用这些术语。哎呀,在 Python 2 天里,B.f 实际上是一个方法(一个未绑定的方法,但与普通函数略有不同),因此在这种特殊情况下,较早的参考资料将它们混为一谈是合理的,特别是因为术语“方法”可以合理地描述在抽象类中定义的函数,即使它实际上还没有绑定到特定实例。
  • @wim:XY 问题是完全可能的。我猜他们放弃了任何参数,因为他们只想在这种无实例情况下使用它,并且给它任何参数会阻止他们甚至试图调用它。他们可能不知道@staticmethod(如果他们知道,他们就会知道@classmethod,并且不需要问这个问题)。

标签: python class inheritance


【解决方案1】:

如果您也希望能够在类上调用函数,而不仅仅是一个实例,并且知道它是在哪个类上调用的(对于一个实例,它被调用的实例的类),使方法aclassmethod,例如:

class A:
    @classmethod
    def f(cls):
        print(cls) 

class B(A):
    pass

The classmethod decorator 使a descriptor 自动提供调用它的类对象,无论它是在类本身还是类的实例上调用,并提供类对象作为第一个参数,而不是实例。它几乎专门用于定义备用构造函数(为了采用__init__ 不接受的一些参数格式,从该格式转换为__init__ 接受,然后以return cls(converted args here) 结尾),但如果你真的坚持上:

  1. 为此使用类,并且
  2. 出于任何原因不创建类的实例

它可以处理两个您的用例(B.f()B().f()); classmethod 在调用时会剥离实例信息,因此后者的行为与前者相同(它们都将 B 作为 cls 的值传递给 f)。

print(__class__) 单独不起作用,因为 __class__ 有意与定义函数的类绑定,作为使无参数 super() 调用起作用的机制的一部分(它使用了一些非常糟糕的 hack 来使 super() 表现得就像是通过传递给函数的第一个位置参数(通常是 self)以及在定义函数的类时偷偷进入闭包范围的 __class__ 变量来调用它在完成定义中);使 __class__ 基于在子类上被调用重新定义自身会将 super() 的所有使用变成无限递归。

【讨论】:

    猜你喜欢
    • 2016-01-16
    • 2017-09-22
    • 1970-01-01
    • 1970-01-01
    • 2021-07-09
    • 2017-04-18
    • 2015-04-23
    • 1970-01-01
    • 2017-12-16
    相关资源
    最近更新 更多