Python抽象属性()“无法用抽象方法实例化抽象类[]”,但我做到了

use*_*190 5 python oop class abstract python-3.x

我正在尝试在 python 3.7 中创建一个具有许多抽象 python 属性的基类。

我使用 @property、@abstractmethod、@property.setter 注释尝试了一种方法(请参阅下面的“开始”)。这有效,但如果子类没有实现 setter,它不会引发异常。这就是我使用 @abstract 的意义所在,所以这没有什么好处。

所以我尝试用另一种方式(参见下面的“结束”)使用两个 @abstractmethod 方法和一个“property()”,它本身不是抽象的,而是使用这些方法。这种方法在实例化子类时会产生错误:

        # {TypeError}Can't instantiate abstract class FirstStep with abstract methods end
Run Code Online (Sandbox Code Playgroud)

我清楚地实现了抽象方法,所以我不明白它的含义。“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
Run Code Online (Sandbox Code Playgroud)

che*_*ner 4

我怀疑这是抽象方法和属性交互中的一个错误。

在您的基类中,按顺序发生以下事情:

  1. 您定义一个名为 的抽象方法start
  2. 您创建一个新属性,使用 1) 中的抽象方法作为其 getter。该名称start现在指的是该属性,唯一引用现在由 持有的原始名称Self.start.fget
  3. Python 保存对 的临时引用start.setter,因为该名称start即将绑定到另一个对象。
  4. 您创建第二个名为的抽象方法start
  5. 3) 中的引用被赋予 4) 中的抽象方法,以定义一个新属性来替换当前绑定到 name 的属性start。该属性的 getter 方法为 1),setter 方法为 4)。现在start指的是这个属性;start.fget指1)中的方法;start.fset参见4)中的方法。

此时,您有了一个属性,其组件函数是抽象方法。属性本身并未被装饰为抽象,但 的定义将property.__isabstractmethod__其标记为抽象,因为它的所有组件方法都是抽象的。更重要的是,您在 中有以下条目Step.__abstractmethods__

  1. start, 这property
  2. end, 这property
  3. set_end,设置者为end
  4. gen_end,吸气剂end

请注意,属性的组件函数start丢失了,因为__abstractmethods__存储的是需要重写的事物的名称,而不是引用。使用property结果属性的setter方法作为装饰器会重复替换名称start所指的内容。

现在,在您的子类中,您定义一个名为 的属性start,隐藏继承的属性,该属性没有setter和一个具体的方法作为其 getter。此时,您是否为该属性提供 setter 并不重要,因为就机器而言abc,您已经提供了它所要求的一切:

  1. 具体的命名方法start
  2. 具体名称get_end和方法set_end
  3. 隐含了 name 的具体定义end,因为该属性的所有底层函数end都已提供具体定义。