python - Python 抽象属性()“无法用抽象方法实例化抽象类 []”,但我做到了
问题描述
我正在尝试在 python 3.7 中创建一个具有许多抽象 python 属性的基类。
我使用@property、@abstractmethod、@property.setter 注释尝试了一种方法(参见下面的“开始”)。这有效,但如果子类没有实现 setter,它不会引发异常。这就是对我使用@abstract 的意义所在,所以这不好。
所以我尝试使用两个@abstractmethod 方法和一个'property()' 以另一种方式(参见下面的'end'),它本身不是抽象的,而是使用这些方法。这种方法在实例化子类时会产生错误:
# {TypeError}Can't instantiate abstract class FirstStep with abstract methods end
我显然正在实现抽象方法,所以我不明白它的含义。'end' 属性未标记为@abstract,但如果我将其注释掉,它会运行(但我没有得到我的属性)。我还添加了测试非抽象方法“test_elapsed_time”以证明我拥有正确的类结构和抽象(它有效)。
有没有可能我在做一些愚蠢的事情,或者 property() 周围是否有一些特殊行为导致了这种情况?
class ParentTask(Task):
def get_first_step(self):
# {TypeError}Can't instantiate abstract class FirstStep with abstract methods end
return FirstStep(self)
class Step(ABC):
# __metaclass__ = ABCMeta
def __init__(self, task):
self.task = task
# First approach. Works, but no warnings if don't implement setter in subclass
@property
@abstractmethod
def start(self):
pass
@start.setter
@abstractmethod
def start(self, value):
pass
# Second approach. "This method for 'end' may look slight messier, but raises errors if not implemented.
@abstractmethod
def get_end(self):
pass
@abstractmethod
def set_end(self, value):
pass
end = property(get_end, set_end)
def test_elapsed_time(self):
return self.get_end() - self.start
class FirstStep(Step):
@property
def start(self):
return self.task.start_dt
# No warnings if this is commented out.
@start.setter
def start(self, value):
self.task.start_dt = value
def get_end(self):
return self.task.end_dt
def set_end(self, value):
self.task.end_dt = value
解决方案
我怀疑这是抽象方法和属性交互中的错误。
在您的基类中,按顺序发生以下事情:
- 您定义了一个名为 的抽象方法
start
。 - 您创建一个使用 1) 中的抽象方法作为其 getter 的新属性。该名称
start
现在引用此属性,唯一引用现在由 . 持有的原始名称Self.start.fget
。 - Python 保存了对 的临时引用
start.setter
,因为该名称start
即将绑定到另一个对象。 - 您创建了第二个抽象方法,名为
start
- 3) 中的引用被赋予了 4) 中的抽象方法来定义一个新属性来替换曾经绑定到 name 的那个
start
。该属性将 1 中的方法作为其 getter,并将 4 中的方法作为其设置器。现在start
指的是这个属性;start.fget
指1)中的方法;start.fset
参考4)中的方法。
此时,您有一个属性,其组件功能是抽象方法。属性本身并没有被装饰成抽象的,但是 的定义将property.__isabstractmethod__
其标记为抽象,因为它的所有组件方法都是抽象的。更重要的是,您有以下条目Step.__abstractmethods__
:
start
, 这property
end
, 这property
set_end
,二传手end
gen_end
, 的吸气剂end
请注意,start
缺少该属性的组件函数,因为__abstractmethods__
存储了需要覆盖的东西的名称,而不是引用。使用property
和结果属性的setter
方法作为装饰器反复替换名称start
所指的内容。
现在,在您的子类中,您定义了一个名为的新start
属性,它隐藏了继承的属性,它没有setter 和一个具体的方法作为它的 getter。此时,您是否为此属性提供 setter 并不重要,因为就abc
机器而言,您已经提供了它所要求的一切:
- 取名的具体方法
start
get_end
名称和具体方法set_end
- 隐含的 name 的具体定义
end
,因为该属性的所有底层函数end
都已提供具体定义。
推荐阅读
- laravel-8 - 在 Laravel 8 中安装 Jetstream 后出现错误
- c - googletest 与全局静态变量
- sql - 将 2 个选择查询合二为一
- elixir - 如何在 Elixir 的编译时指定模块实现?
- java - 无法构建 Appium 服务器,因为它无法找到 NodeInCurrentFileSystem
- git - git 在依赖于未合并分支的小分支上工作
- python - 将一段持续时间分成几行,最多不超过 24 小时
- algorithm - 什么是平凡和非平凡的启发式?
- vue.js - Vutify 列表组使父级可点击
- python - 通过 tags_metadata 字典定义 FastApi 中标签的顺序