class 装饰器禁用 __init_subclass__
class decorator disables __init_subclass__
我四处寻找这个问题的答案,但没有找到任何答案。如果之前有人问过这个问题,我深表歉意。
我知道从 parent class 强制执行 child class 的给定方法的 3-4 种方法中(编辑 __new__
metaclass 的方法,连接到 builtins.__build_class__
,使用 __init_subclass__
或使用 abc.abstractmethod
)我通常最终使用 __init_subclass__
,主要是因为易于使用 and,与 @abc.abstractmethod
不同,child class 上的约束是根据 child class 定义而不是 class 实例化检查的。示例:
class Par():
def __init_subclass__(self, *args, **kwargs):
must_have = 'foo'
if must_have not in list(self.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
class Chi(Par):
def __init__(self):
super().__init__()
此示例代码显然会引发错误,因为 Chi
没有 foo
方法。尽管如此,我还是发现了这样一个事实,即通过使用一个简单的 class 装饰器,来自上游 class 的约束可以是 by-passed:
def add_hello_world(Cls):
class NewCls(object):
def __init__(self, *args, **kwargs):
self.instance = Cls(*args, **kwargs)
def hello_world(self):
print("hello world")
return NewCls
@add_hello_world
class Par:
def __init_subclass__(self, *args, **kwargs):
must_have = "foo"
if must_have not in list(self.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
class Chi(Par):
def __init__(self):
super().__init__()
c = Chi()
c.hello_world()
以上代码运行没有问题。现在,忽略我装饰的 class 是 Par
这一事实(当然,如果 Par
是库代码,我什至可能无法作为用户代码开发人员访问它) ,我无法真正解释这种行为。对我来说很明显,可以使用装饰器向现有的 class 添加方法或功能,但我从未见过不相关的装饰器(只打印 hello world
,甚至不会弄乱 class 创建) 禁用已经存在于 class.
中的方法
这是有意的 Python 行为吗?或者这是某种错误?老实说,据我所知,这可能会带来一些安全问题。
这是否只发生在 __init_subclass__
数据模型上?或者也给其他人?
记住,装饰器语法只是函数应用:
class Par:
def __init_subclass__(...):
...
Par = add_hello_world(Par)
class原本绑定到Par
定义__init_subclass__
;在 add_hello_world
中定义的 new class 没有,这就是 post-修饰名称 Par
的 class指的是,class你是subclassing.
顺便说一句,您仍然可以通过 __init__
.
访问原始 class Par
显式调用装饰器:
class Par:
def __init_subclass__(self, *args, **kwargs):
must_have = "foo"
if must_have not in list(self.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
Foo = Par # Keep this for confirmation
Par = add_hello_world(Par)
我们可以确认闭包保留了对原始 class:
的引用
>>> Par.__init__.__closure__[0].cell_contents
<class '__main__.Par'>
>>> Par.__init__.__closure__[0].cell_contents is Par
False
>>> Par.__init__.__closure__[0].cell_contents is Foo
True
如果您确实尝试将其子化class,您将得到预期的错误:
>>> class Bar(Par.__init__.__closure__[0].cell_contents):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "tmp.py", line 16, in __init_subclass__
raise AttributeError(f"Must have {must_have}")
AttributeError: Must have foo
我四处寻找这个问题的答案,但没有找到任何答案。如果之前有人问过这个问题,我深表歉意。
我知道从 parent class 强制执行 child class 的给定方法的 3-4 种方法中(编辑 __new__
metaclass 的方法,连接到 builtins.__build_class__
,使用 __init_subclass__
或使用 abc.abstractmethod
)我通常最终使用 __init_subclass__
,主要是因为易于使用 and,与 @abc.abstractmethod
不同,child class 上的约束是根据 child class 定义而不是 class 实例化检查的。示例:
class Par():
def __init_subclass__(self, *args, **kwargs):
must_have = 'foo'
if must_have not in list(self.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
class Chi(Par):
def __init__(self):
super().__init__()
此示例代码显然会引发错误,因为 Chi
没有 foo
方法。尽管如此,我还是发现了这样一个事实,即通过使用一个简单的 class 装饰器,来自上游 class 的约束可以是 by-passed:
def add_hello_world(Cls):
class NewCls(object):
def __init__(self, *args, **kwargs):
self.instance = Cls(*args, **kwargs)
def hello_world(self):
print("hello world")
return NewCls
@add_hello_world
class Par:
def __init_subclass__(self, *args, **kwargs):
must_have = "foo"
if must_have not in list(self.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
class Chi(Par):
def __init__(self):
super().__init__()
c = Chi()
c.hello_world()
以上代码运行没有问题。现在,忽略我装饰的 class 是 Par
这一事实(当然,如果 Par
是库代码,我什至可能无法作为用户代码开发人员访问它) ,我无法真正解释这种行为。对我来说很明显,可以使用装饰器向现有的 class 添加方法或功能,但我从未见过不相关的装饰器(只打印 hello world
,甚至不会弄乱 class 创建) 禁用已经存在于 class.
这是有意的 Python 行为吗?或者这是某种错误?老实说,据我所知,这可能会带来一些安全问题。
这是否只发生在
__init_subclass__
数据模型上?或者也给其他人?
记住,装饰器语法只是函数应用:
class Par:
def __init_subclass__(...):
...
Par = add_hello_world(Par)
class原本绑定到Par
定义__init_subclass__
;在 add_hello_world
中定义的 new class 没有,这就是 post-修饰名称 Par
的 class指的是,class你是subclassing.
顺便说一句,您仍然可以通过 __init__
.
Par
显式调用装饰器:
class Par:
def __init_subclass__(self, *args, **kwargs):
must_have = "foo"
if must_have not in list(self.__dict__.keys()):
raise AttributeError(f"Must have {must_have}")
def __init__(self):
pass
Foo = Par # Keep this for confirmation
Par = add_hello_world(Par)
我们可以确认闭包保留了对原始 class:
的引用>>> Par.__init__.__closure__[0].cell_contents
<class '__main__.Par'>
>>> Par.__init__.__closure__[0].cell_contents is Par
False
>>> Par.__init__.__closure__[0].cell_contents is Foo
True
如果您确实尝试将其子化class,您将得到预期的错误:
>>> class Bar(Par.__init__.__closure__[0].cell_contents):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "tmp.py", line 16, in __init_subclass__
raise AttributeError(f"Must have {must_have}")
AttributeError: Must have foo