我正在尝试让类装饰工作。装饰器将向其应用的类添加__init_subclass__
方法。
但是,当该方法动态添加到类中时,第一个参数不会绑定到子类对象。为什么会这样?
作为一个例子:这是有效的,下面的静态代码是我想要最终得到的一个例子:
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__
方法,则子类不会绑定到第一个参数:
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'
为什么会发生这种情况,我该如何做到这一点并让绑定以正确的方式运行?
答案 0 :(得分:1)
__init_subclass__
is an implicit classmethod
如果你想了解原因,可能无法使用零参数super(读here),但你应该能够在装饰器本身内显式绑定super。
head: {
title: 'mynuxt',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: 'Nuxt.js project' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' },
{ rel: 'stylesheet', href: '/css/bootstrap.min.css' },
{ rel: 'stylesheet', href: '/css/mdb.min.css' },
{ rel: 'stylesheet', href: '/css/style.min.css' },
],
script: [
{ src: '/js/bootstrap.min.js' },
{ src: '/js/popper.min.js' },
{ src: '/js/mdb.min.js' }
],
},
答案 1 :(得分:0)
对那些主张使用abc
的人来说只是一个评论。尽管abc
也可以解决问题,但值得一提的是两种方法之间有两个区别(我知道):
类定义与实例化。
abc.abstractmethod
装饰器在类实例化时对子类施加约束,而__init_subclass__
数据模型已经在类定义时进行了约束。示例:
class Foo(abc.ABC):
def init(self):
pass
@abc.abstractmethod
def foo():
pass
class Bar(Foo):
pass
此代码将毫无问题地进行编译。当您通过以下方式调用子类的构造函数时,该错误将首先显示。 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__()
将引发错误,因为检查是在类定义时执行的。
强制执行继承级别
另一个区别是abstractmethod
希望覆盖修饰的方法一次,但是__init_subclass__
数据模型也将对子类的子类强制执行约束。示例:
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方法,即使介于两者之间的类具有foo方法。