考虑一个带有函数的抽象基类,您希望每个后续子类都覆盖该函数。使用 abc 模块和 ABCMeta;是否使用@abstractproperty
或@abstractmethod
实际上强制子类/开发人员实现创建装饰器指定的函数类型?从我的实验中,您可以使用子类中的方法覆盖抽象属性和使用属性的抽象方法。
这个概念不正确吗?
考虑一个带有函数的抽象基类,您希望每个后续子类都覆盖该函数。使用 abc 模块和 ABCMeta;是否使用@abstractproperty
或@abstractmethod
实际上强制子类/开发人员实现创建装饰器指定的函数类型?从我的实验中,您可以使用子类中的方法覆盖抽象属性和使用属性的抽象方法。
这个概念不正确吗?
这个概念是正确的;该ABCMeta
代码不区分 aabstractproperty
和 a abstractmethod
。
这两个装饰器都为被装饰的项目添加了一个属性.__isabstractmethod__
,ABCMeta
用于将.__abstractmethods__
属性 (a frozenset
) 添加到您定义的 ABC。然后,该object
类型会防止创建任何类的实例,其中列出的任何名称.__abstractmethods__
都没有具体实现。没有检查那里的功能与属性。
为了显示:
>>> from abc import *
>>> class C:
... __metaclass__ = ABCMeta
... @abstractmethod
... def abstract_method(self): pass
... @abstractproperty
... def abstract_property(self): return 'foo'
...
>>> C.__abstractmethods__
frozenset(['abstract_method', 'abstract_property'])
通过在子类中为这些创建新的覆盖,ABCMeta
该类将使用该属性找到更少的方法或. __isabstractmethod__
属性,从而使结果__abstractmethods__
集更小;一旦集合为空,您就可以创建此类子类的实例。
这些检查在ABCMeta.__new__
构造函数中进行,并且不进行匹配描述符类型的检查:
cls = super(ABCMeta, mcls).__new__(mcls, name, bases, namespace)
# Compute set of abstract method names
abstracts = set(name
for name, value in namespace.items()
if getattr(value, "__isabstractmethod__", False))
for base in bases:
for name in getattr(base, "__abstractmethods__", set()):
value = getattr(cls, name, None)
if getattr(value, "__isabstractmethod__", False):
abstracts.add(name)
cls.__abstractmethods__ = frozenset(abstracts)
您必须创建一个ABCMeta
覆盖该__new__
方法的子类,并检查基类上命名的任何抽象方法或属性是否确实与非抽象方法或属性匹配cls
。