首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >修补__init_subclass__

修补__init_subclass__
EN

Stack Overflow用户
提问于 2020-12-08 19:01:56
回答 2查看 156关注 0票数 2

我在修补自定义类‘__init_subclass__’时遇到了麻烦。我认为这与我将修补的函数绑定到类的方式有关:

代码语言:javascript
复制
def _patched_initsubclass(cls, **kwargs):
    print(f"CLS from subclassing A: {cls}")
    super(cls, cls).__init_subclass__(**kwargs)

class A: ...

A.__init_subclass__ = _patched_initsubclass.__get__(A, A)

class B(A): ...  # Output: CLS from subclassing A: <class '__main__.A'>

但是,我知道正确设置的__init_subclass__应该有不同的输出:

代码语言:javascript
复制
class F:

    def __init_subclass__(cls, **kwargs):
        print(f"CLS from subclassing F: {cls}")
        pass

class C(F): ...  # Output: CLS from subclassing F: <class '__main__.C'>

也就是说,超类的cls定义中的__init_subclass__应该是子类时的子类。我试图通过不同的所以posts和文档找到正确的方法来绑定dunder,但是没有找到正确的方法来实现它。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-08 21:37:02

您对super的使用是无效的;它应该传递正在调用它的类的类型(例如在其中定义的类)和传递的实际类型(调用它的类),所以super(cls, cls)是在撒谎;您明确使用描述符协议函数__get__将它预先绑定到A (当它在B上被调用时绕过描述符协议),所以它总是说“我是用一个AA调用的”,即使它实际上是在其他事情上被调用的。

你想要的并不容易做正确的方式;super(cls, None),仍然是错误的,即使它碰巧在这里工作。您告诉super遍历None对象的MRO,并调用它在MRO中的B之后找到的第一个__init_subclass__。显然,尽管B不在MRO中(这应该是一个错误根据医生的说法:“如果第二个参数是对象,则isinstance(obj, type)必须是真。如果第二个参数是类型,则issubclass(type2, type)必须是真。”;c‘’est la vie),它默默地返回object.__init_subclass__并调用它;它的工作只因为object.__init_subclass__什么都不做,也不反对被调用。

正确做到这一点的唯一方法是为每个类创建一个新版本的_patched_initsubclass,以修补它正在修补的类。此外,在这样做时,您可以通过在新方法的闭包范围中放置super()来启用零arg __class__ (零arg super()魔术是通过编译器实现的,在引用__class__super的类中定义的所有函数实际上都是闭包,而__class__在闭包作用域中是可见的)。

一个解决方案是:

代码语言:javascript
复制
def make_patched_initsubclass_for(__class__):  # Receive class to patch as __class__ directly
    # Same as before, just defined inside function to get closure scope,
    # and super() is called with no arguments
    def _patched_initsubclass(cls, **kwargs):
        print(f"CLS from subclassing A: {cls}")
        super().__init_subclass__(**kwargs)    # super() roughly equivalent to super(__class__, cls)

    # Returns a classmethod so it descriptor protocol
    # knows to provide class uniformly, never an instance
    return classmethod(_patched_initsubclass)

class A: ...

A.__init_subclass__ = make_patched_initsubclass_for(A)  # Produces valid closure for binding to A

class B(A): ...  # Output CLS from subclassing A: <class '__main__.B'>

如果您没有将参数命名为make_patched_initsubclass_for __class__ (将其命名为patched_cls或somesuch),则必须使用super(patched_cls, cls)而不是super(),但这两种方法都会起作用。

票数 2
EN

Stack Overflow用户

发布于 2020-12-08 21:13:13

我找到了一个不涉及通过__get__绑定路径函数的解决方案

代码语言:javascript
复制
def _patched_initsubclass(cls, **kwargs):
    print(f"CLS from subclassing A: {cls}")

    super(cls, None).__init_subclass__(**kwargs)


class A: ...


A.__init_subclass__ = classmethod(_patched_initsubclass)


class B(A): ...  # Output CLS from subclassing A: <class '__main__.B'>

我仍然不清楚这是为什么:即classmethod()和直接绑定__get__之间的区别是什么。

答案可能与classmethod在幕后做什么有关,所以我会研究这个问题。

我会留下这个答案给其他任何人,可能会发现它有帮助,并将包括任何后续信息。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65205205

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档