Python抽象属性()“不能用抽象方法实例化抽象类[]",但我做到了 [英] Python abstract property() "Can't instantiate abstract class [] with abstract methods", but I did

查看:46
本文介绍了Python抽象属性()“不能用抽象方法实例化抽象类[]",但我做到了的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to create a base class with a number of abstract python properties, in python 3.7.

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

I tried it one way (see 'start' below) using the @property, @abstractmethod, @property.setter annotations. This worked but it doesn't raise an exception if the subclass doesn't implement a setter. That's the point of using @abstract to me, so that's no good.

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

So I tried doing it another way (see 'end' below) using two @abstractmethod methods and a 'property()', which is not abstract itself but uses those methods. This approach generates an error when instantiating the subclass:

        # {TypeError}Can't instantiate abstract class FirstStep with abstract methods end

我清楚地实现了抽象方法,所以我不明白这是什么意思.'end' 属性未标记为@abstract,但如果我将其注释掉,它确实会运行(但我没有得到我的属性).我还添加了测试非抽象方法test_elapsed_time"以证明我拥有正确的类结构和抽象(它有效).

I'm clearly implementing the abstract methods, so I don't understand what it means. The 'end' property is not marked @abstract, but if I comment it out, it does run (but I don't get my property). I also added that test non-abstract method 'test_elapsed_time' to demonstrate I have the class structure and abstraction right (it works).

是否有可能我在做一些愚蠢的事情,或者是否有一些围绕 property() 的特殊行为导致了这种情况?

Any chance I'm doing something dumb, or is there some special behavior around property() that's causing this?

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

推荐答案

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

I suspect this is a bug in the interaction of abstract methods and properties.

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

In your base class, the following things happen, in order:

  1. 您定义了一个名为 start 的抽象方法.
  2. 您创建了一个新属性,该属性使用 1) 中的抽象方法作为其 getter.名称 start 现在引用此属性,唯一引用现在由 Self.start.fget 保存的原始名称.
  3. Python 保存了对 start.setter 的临时引用,因为名称 start 即将绑定到另一个对象.
  4. 您创建了第二个名为 start
  5. 的抽象方法
  6. 来自 3) 的引用被赋予了来自 4) 的抽象方法来定义一个新属性来替换当前绑定到名称 start 的一次.该属性的 getter 方法是 1 中的方法,setter 方法是 4).现在 start 指的是这个属性;start.fget 参考1)中的方法;start.fset 参考 4 中的方法).
  1. You define an abstract method named start.
  2. You create a new property that uses the abstract method from 1) as its getter. The name start now refers to this property, with the only reference to the original name now held by Self.start.fget.
  3. Python saves a temporary reference to start.setter, because the name start is about to be bound to yet another object.
  4. You create a second abstract method named start
  5. The reference from 3) is given the abstract method from 4) to define a new property to replace the once currently bound to the name start. This property has as its getter the method from 1 and as its setter the method from 4). Now start refers to this property; start.fget refers to the method from 1); start.fset refers to the method from 4).

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

At this point, you have a property, whose component functions are abstract methods. The property itself was not decorated as abstract, but the definition of property.__isabstractmethod__ marks it as such because all its component methods are abstract. More importantly, you have the following entries in Step.__abstractmethods__:

  1. start属性
  2. end属性
  3. set_endend
  4. 的 setter
  5. gen_endend
  6. 的 getter
  1. start, the property
  2. end, the property
  3. set_end, the setter for end
  4. gen_end, the getter for end

注意 start 属性的组件函数缺失了,因为 __abstractmethods__ 存储了名称,而不是引用,需要的东西被覆盖.使用 property 和结果属性的 setter 方法作为装饰器重复替换 start 所指的名称.

Note that the component functions for the start property are missing, because __abstractmethods__ stores names of, not references to, things that need to be overriden. Using property and the resulting property's setter method as decorators repeatedly replace what the name start refers to.

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

Now, in your child class, you define a new property named start, shadowing the inherited property, which has no setter and a concrete method as its getter. At this point, it doesn't matter if you provide a setter for this property or not, because as far as the abc machinery is concerned, you have provided everything it asked for:

  1. 名称的具体方法start
  2. 名称 get_endset_end
  3. 的具体方法
  4. 对名称end 的隐式具体定义,因为属性end 的所有底层函数都已提供具体定义.
  1. A concrete method for the name start
  2. Concrete methods for the names get_end and set_end
  3. Implicitly a concrete definition for the name end, because all of the underlying functions for the property end have been provided concrete definitions.

这篇关于Python抽象属性()“不能用抽象方法实例化抽象类[]",但我做到了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆