gpt4 book ai didi

python - 为什么 abc.ABCMeta 抽象实例化检查不适用于 `list` 和 `dict` 的派生?

转载 作者:太空宇宙 更新时间:2023-11-03 23:56:46 24 4
gpt4 key购买 nike

我一直在用 python 中的 abc 模块进行一些试验。啦啦啦

>>> import abc

在正常情况下,如果 ABC 类包含未实现的 abstractmethod,您希望它不会被实例化。你知道如下:

>>> class MyClass(metaclass=abc.ABCMeta):
... @abc.abstractmethod
... def mymethod(self):
... return -1
...
>>> MyClass()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyClass with abstract methods mymethod

OR 对于任何派生类。这一切似乎都工作正常,直到你从某些东西继承......说 dictlist 如下:

>>> class YourClass(list, metaclass=abc.ABCMeta):
... @abc.abstractmethod
... def yourmethod(self):
... return -1
...
>>> YourClass()
[]

这是令人惊讶的,因为 type 可能是主要的工厂或元类 -ish 东西,或者我从以下假设。

>>> type(abc.ABCMeta)
<class 'type'>
>>> type(list)
<class 'type'>

通过一些调查,我发现它可以归结为一些简单的事情,例如将 __abstractmethod__ 属性添加到类的 object 中,其余的将自行发生:

>>> class AbstractClass:
... pass
...
>>> AbstractClass.__abstractmethods__ = {'abstractmethod'}
>>> AbstractClass()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class AbstractClass with abstract methods abstractmethod

因此,可以通过故意覆盖 __new__ 方法并清除 __abstractmethods__ 来简单地避免检查,如下所示:

>>> class SupposedlyAbstractClass(metaclass=abc.ABCMeta):
... def __new__(cls):
... cls.__abstractmethods__ = {}
... return super(AbstractClass, cls).__new__(cls)
... @abc.abstractmethod
... def abstractmethod(self):
... return -1
...
>>> SupposedlyAbstractClass()
<__main__.SupposedlyAbstractClass object at 0x000001FA6BF05828>

这种行为在 Python 2.7 和 Python 3.7 中是相同的,正如我亲自检查过的那样。我不知道这是否适用于所有其他 python 实现。

最后,问题来了……为什么要这样做?我们永远不应该从 listtupledict 中创建抽象类是否明智?或者我应该继续添加一个 __new__ 类方法在实例化之前检查 __abstractmethods__ 吗?

最佳答案

问题

如果你有下一节课:

from abc import ABC, abstractmethod
class Foo(list, ABC):
@abstractmethod
def yourmethod(self):
pass

问题是 Foo 的对象 可以 创建而不会出现任何错误,因为 Foo.__new__(Foo) 将调用直接委托(delegate)给list.__new__(Foo) 而不是 ABC.__new__(Foo) (负责检查所有抽象方法是否在将要实例化的类中实现)

我们可以在 Foo 上实现 __new__ 并尝试调用 ABC.__new__:

class Foo(list, ABC):
def __new__(cls, *args, **kwargs):
return ABC.__new__(cls)

@abstractmethod
def yourmethod(self):
pass
Foo()

但是他引发了下一个错误:

TypeError: object.__new__(Foo) is not safe, use list.__new__()

这是由于 ABC.__new__(Foo) 调用了 object.__new__(Foo) 这在 Foo 继承时似乎是不允许的来自 list

可能的解决方案

您可以在 Foo.__new__ 上添加额外的代码,以检查要实例化的类中的所有抽象方法是否都已实现(基本上完成 ABC.__new__ 的工作) >).

像这样:

class Foo(list, ABC):
def __new__(cls, *args, **kwargs):
if hasattr(cls, '__abstractmethods__') and len(cls.__abstractmethods__) > 0:
raise TypeError(f"Can't instantiate abstract class {cls.__name__} with abstract methods {', '.join(cls.__abstractmethods__)}")
return super(Foo, cls).__new__(cls)


@abstractmethod
def yourmethod(self):
return -1

现在 Foo() 引发错误。但是接下来的代码运行没有任何问题:

class Bar(Foo):
def yourmethod(self):
pass
Bar()

关于python - 为什么 abc.ABCMeta 抽象实例化检查不适用于 `list` 和 `dict` 的派生?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57407909/

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