cow*_*tor 15 python abstract python-3.x python-decorators
如何在 python 中创建抽象属性?
import abc
class MyClass(abc.ABC):
@abc.abstractmethod
@property
def foo(self):
pass
Run Code Online (Sandbox Code Playgroud)
结果出现错误AttributeError: attribute '__isabstractmethod__' of 'property' objects is not writable
cow*_*tor 22
事实证明,对于 Python 装饰器来说,顺序很重要。
@abc.abstractmethod
@property
Run Code Online (Sandbox Code Playgroud)
不等于
@property
@abc.abstractmethod
Run Code Online (Sandbox Code Playgroud)
创建抽象属性的正确方法是:
import abc
class MyClass(abc.ABC):
@property
@abc.abstractmethod
def foo(self):
pass
Run Code Online (Sandbox Code Playgroud)
长话短说:
cocklinator 是正确的,订单很重要。
正式:
当 Abstractmethod() 与其他方法描述符结合使用时,应将其作为最内层的装饰器应用,如以下使用示例所示:...
(粗体是我强调的:不仅顺序很重要,而且abstractmethod必须是最里面的)
不太令人满意的解释:
装饰器就像 和@property一样,@abstractmethod是包装它们所装饰的函数(方法)的函数。因此,两个装饰器会产生嵌套的包装,其中最接近函数定义的一个(编辑器中最低的)是最里面的包装函数。
在@property装饰器中,__isabstractmethod__内部属性被声明为属性本身,只有 getter,没有 setter - 使其成为只读。property这是来自in的 python 代码builtins.py
__isabstractmethod__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default
Run Code Online (Sandbox Code Playgroud)
第一个 arg 是 getter,第二个是 setter - 在本例中为 None - 如果您不相信我,请参阅此处的构造函数:
def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
Run Code Online (Sandbox Code Playgroud)
在@abstractmethod包装中,__isabstractmethod__属性设置为True。看这里:
funcobj.__isabstractmethod__ = True
Run Code Online (Sandbox Code Playgroud)
这是来自abc模块,funcobj正在装饰的方法在哪里。现在,通常这就是您想要的抽象方法,但在本例中,它是包装原始方法的属性装饰器函数。正如我们在上面看到的,该函数变为__isabstractmethod__只读。
我说“不太令人满意”是因为没有适当的理由——只有晦涩的文档,除非遇到问题,否则你找不到。我猜这实际上只是装饰器工作方式的限制。
对于额外的背景信息,我在 GitHub 中跟踪了这一更改(一定喜欢开源!),其中包括对这些发行说明的更新,其中声明 - 也没有太多解释:
abc.abstractproperty 已被弃用,请使用 abc.abstractmethod() 的属性。
对于所获得的异常的深层来源,您可以在此处查看 python 描述符的 C 源代码,并向下搜索“不可写”以查看它在尝试生成 setter 时在何处抛出异常。
| 归档时间: |
|
| 查看次数: |
3892 次 |
| 最近记录: |