动态 __init_subclass__ 方法的参数绑定
argument binding for dynamic __init_subclass__ method
我正在尝试让 class 装饰器工作。装饰器将添加一个 __init_subclass__
方法到它所应用的 class。
但是,当该方法动态添加到 class 时,第一个参数未绑定到子 class 对象。为什么会这样?
举个例子:这行得通,下面的静态代码是我试图最终得到的示例:
class C1:
def __init_subclass__(subcls, *args, **kwargs):
super().__init_subclass__(*args, **kwargs)
print(f"init_subclass -> {subcls.__name__}, {args!r}, {kwargs!r}")
测试:
>>> D = type("D", (C1,), {})
init_subclass -> D, (), {}
但是,如果我动态添加 __init__subclass__
方法,子 class 不会绑定到第一个参数:
def init_subclass(subcls, **kwargs):
super().__init_subclass__(**kwargs)
print(f"init_subclass -> {subcls.__name__}, {args!r}, {kwargs!r}")
def decorator(Cls):
Cls.__init_subclass__ = init_subclass
return Cls
@decorator
class C2:
pass
测试:
>>> D = type("D", (C2,), {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: init_subclass() missing 1 required positional argument: 'subcls'
为什么会发生这种情况,我该如何做到这一点并使绑定以正确的方式工作?
__init_subclass__
is an implicit classmethod.
可能无法使用零参数 super(如果您想了解原因,请阅读 ),但您应该能够在装饰器本身内部显式绑定 super。
def decorator(Cls):
def __init_subclass__(subcls, **kwargs):
print(f'init subclass {Cls!r}, {subcls!r}, {kwargs!r}')
super(Cls, subcls).__init_subclass__(**kwargs)
Cls.__init_subclass__ = classmethod(__init_subclass__)
return Cls
@decorator
class C:
pass
class D(C):
pass
只是对那些提倡使用 abc
的人的评论。虽然 abc
也可以解决关键问题,但值得一提的是,这两种方法之间有两个区别(据我所知):
Class 定义与实例化。
abc.abstractmethod
装饰器在 class 实例化时对子 class 强制执行约束,而 __init_subclass__
数据模型已经在 class 定义中执行了.示例:
class Foo(abc.ABC):
def init(self):
pass
@abc.abstractmethod
def foo():
pass
class Bar(Foo):
pass
这段代码可以毫无问题地编译。当您通过例如 class 调用子 class 的构造函数时,该错误将首先出现x = Bar()
。如果这是库代码,这意味着错误直到运行时才会出现。另一方面,以下代码:
class Par():
def __init_subclass__(cls, *args, **kwargs):
must_have = 'foo'
if must_have not in list(cls.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
class Chi(Par):
def __init__(self):
super().__init__()
会抛出错误,因为检查是在 class 定义时执行的。
通过继承级别强制实施
另一个区别是 abstractmethod
希望修饰的方法被覆盖一次,但是 __init_subclass__
数据模型也会对子 class 的子强制执行约束。示例:
class Foo(abc.ABC):
def __init__(self):
pass
@abc.abstractmethod
def foo():
pass
class Bar(Foo):
def __init__(self):
super().__init__()
def foo(self):
pass
class Mai(Bar):
pass
x = Mai()
这段代码可以工作。 Mai
不需要 foo 方法,因为抽象方法已在 Bar
中被覆盖。另一方面:
class Par():
def __init_subclass__(cls, *args, **kwargs):
must_have = 'foo'
if must_have not in list(cls.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
class Chi(Par):
def __init__(self):
super().__init__()
def foo(self):
pass
class Chichi(Chi):
def __init__(self):
super().__init__()
这将引发错误,因为 Chichi
还必须有一个 foo 方法,即使中间的 class 有一个。
我正在尝试让 class 装饰器工作。装饰器将添加一个 __init_subclass__
方法到它所应用的 class。
但是,当该方法动态添加到 class 时,第一个参数未绑定到子 class 对象。为什么会这样?
举个例子:这行得通,下面的静态代码是我试图最终得到的示例:
class C1:
def __init_subclass__(subcls, *args, **kwargs):
super().__init_subclass__(*args, **kwargs)
print(f"init_subclass -> {subcls.__name__}, {args!r}, {kwargs!r}")
测试:
>>> D = type("D", (C1,), {})
init_subclass -> D, (), {}
但是,如果我动态添加 __init__subclass__
方法,子 class 不会绑定到第一个参数:
def init_subclass(subcls, **kwargs):
super().__init_subclass__(**kwargs)
print(f"init_subclass -> {subcls.__name__}, {args!r}, {kwargs!r}")
def decorator(Cls):
Cls.__init_subclass__ = init_subclass
return Cls
@decorator
class C2:
pass
测试:
>>> D = type("D", (C2,), {})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: init_subclass() missing 1 required positional argument: 'subcls'
为什么会发生这种情况,我该如何做到这一点并使绑定以正确的方式工作?
__init_subclass__
is an implicit classmethod.
可能无法使用零参数 super(如果您想了解原因,请阅读
def decorator(Cls):
def __init_subclass__(subcls, **kwargs):
print(f'init subclass {Cls!r}, {subcls!r}, {kwargs!r}')
super(Cls, subcls).__init_subclass__(**kwargs)
Cls.__init_subclass__ = classmethod(__init_subclass__)
return Cls
@decorator
class C:
pass
class D(C):
pass
只是对那些提倡使用 abc
的人的评论。虽然 abc
也可以解决关键问题,但值得一提的是,这两种方法之间有两个区别(据我所知):
Class 定义与实例化。
abc.abstractmethod
装饰器在 class 实例化时对子 class 强制执行约束,而 __init_subclass__
数据模型已经在 class 定义中执行了.示例:
class Foo(abc.ABC):
def init(self):
pass
@abc.abstractmethod
def foo():
pass
class Bar(Foo):
pass
这段代码可以毫无问题地编译。当您通过例如 class 调用子 class 的构造函数时,该错误将首先出现x = Bar()
。如果这是库代码,这意味着错误直到运行时才会出现。另一方面,以下代码:
class Par():
def __init_subclass__(cls, *args, **kwargs):
must_have = 'foo'
if must_have not in list(cls.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
class Chi(Par):
def __init__(self):
super().__init__()
会抛出错误,因为检查是在 class 定义时执行的。
通过继承级别强制实施
另一个区别是 abstractmethod
希望修饰的方法被覆盖一次,但是 __init_subclass__
数据模型也会对子 class 的子强制执行约束。示例:
class Foo(abc.ABC):
def __init__(self):
pass
@abc.abstractmethod
def foo():
pass
class Bar(Foo):
def __init__(self):
super().__init__()
def foo(self):
pass
class Mai(Bar):
pass
x = Mai()
这段代码可以工作。 Mai
不需要 foo 方法,因为抽象方法已在 Bar
中被覆盖。另一方面:
class Par():
def __init_subclass__(cls, *args, **kwargs):
must_have = 'foo'
if must_have not in list(cls.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
class Chi(Par):
def __init__(self):
super().__init__()
def foo(self):
pass
class Chichi(Chi):
def __init__(self):
super().__init__()
这将引发错误,因为 Chichi
还必须有一个 foo 方法,即使中间的 class 有一个。