Python抽象property()“无法使用抽象方法实例化抽象类[]",但我做到了 [英] Python abstract property() "Can't instantiate abstract class [] with abstract methods", but I did
问题描述
我正在尝试在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:
- 您定义了一个名为
start
的抽象方法. - 您创建一个新属性,该属性使用1)中的抽象方法作为其吸气剂.现在,名称
start
引用了此属性,并且仅引用了现在由Self.start.fget
保存的原始名称. - Python保存对
start.setter
的临时引用,因为名称start
即将绑定到另一个对象. - 您创建另一个名为
start
的抽象方法 - 3)的引用被赋予4)的抽象方法,以定义一个新属性来替换当前绑定到名称
start
的属性.此属性具有从1开始的方法作为其getter,从4)开始作为其设置器.现在,start
引用了此属性;start.fget
是指1)中的方法;start.fset
是指4)中的方法.
- You define an abstract method named
start
. - 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 bySelf.start.fget
. - Python saves a temporary reference to
start.setter
, because the namestart
is about to be bound to yet another object. - You create a second abstract method named
start
- 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). Nowstart
refers to this property;start.fget
refers to the method from 1);start.fset
refers to the method from 4).
这时,您有了一个属性,其 component 函数是抽象方法.该属性本身没有被修饰为抽象的,但是 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__
:
-
开始
,属性
-
end
,属性
-
set_end
,end
的设置器 -
gen_end
,end
的获取者
start
, theproperty
end
, theproperty
set_end
, the setter forend
gen_end
, the getter forend
请注意,缺少 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
的 new 属性,以隐藏继承的属性,该属性具有 no 设置器和一个具体方法作为其获取方法.此时,是否为该属性提供设置器都没有关系,因为就 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:
- 名称
start
的具体方法 - 名称为
get_end
和set_end
的具体方法 - 由于名称
end
的所有基础函数都已提供了具体定义,因此对名称end
进行了具体定义.
- A concrete method for the name
start
- Concrete methods for the names
get_end
andset_end
- Implicitly a concrete definition for the name
end
, because all of the underlying functions for the propertyend
have been provided concrete definitions.
这篇关于Python抽象property()“无法使用抽象方法实例化抽象类[]",但我做到了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!