gpt4 book ai didi

python - 类装饰器禁用 __init_subclass__

转载 作者:行者123 更新时间:2023-11-28 22:09:13 26 4
gpt4 key购买 nike

我四处寻找这个问题的答案,但找不到任何东西。如果之前有人问过这个问题,我深表歉意。

我知道有 3-4 种方法可以从父类强制执行子类的给定方法(编辑元类的 __new__ 方法,挂接到 builtins.__build_class__,使用 __init_subclass__ 或使用 abc.abstractmethod)我通常最终使用 __init_subclass__,主要是因为易于使用,而且与@abc.abstractmethod,子类的约束是根据子类定义而不是类实例化来检查的。示例:

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 方法。尽管如此,我还是发现了一个事实,即可以使用一个简单的类装饰器来绕过上游类的这个约束:

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()

上面的代码运行没有问题。现在,忽略我装饰的类是 Par 这一事实(当然,如果 Par 是库代码,我什至可能无法将其作为用户代码访问开发人员),我无法真正解释这种行为。对我来说很明显,可以使用装饰器向现有类添加方法或功能,但我从未见过不相关的装饰器(只是打印 hello world,甚至不会混淆类创建)禁用类中已经存在的方法。

  • 这是预期的 Python 行为吗?或者这是某种错误?老实说,据我所知,这可能会带来一些安全问题。

  • 这是否只发生在 __init_subclass__ 数据模型上?还是也给其他人?

最佳答案

记住,装饰器语法只是函数应用:

class Par:
def __init_subclass__(...):
...


Par = add_hello_world(Par)

最初绑定(bind)到 Par 的类定义了 __init_subclass__add_hello_world 中定义的new 类没有,就是后修饰名Par 所指的类,你是的类子类化。


顺便说一句,您仍然可以通过 __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)

我们可以确认闭包保留了对原始类的引用:

>>> 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 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

关于python - 类装饰器禁用 __init_subclass__,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57811837/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com