Python抽象属性();Can';t使用抽象方法实例化抽象类[];,但我做到了
我试图在Python3.7中创建一个基类,其中包含许多抽象python属性 我用@property、@abstractmethod、@property.setter注释尝试了一种方法(请参见下面的“开始”)。这是可行的,但如果子类未实现setter,则不会引发异常。对我来说,这就是使用@abstract的意义所在,所以这是不好的 所以我尝试了另一种方法(见下面的“end”),使用两个@abstractmethod方法和一个“property()”,它本身不是抽象的,而是使用这些方法。此方法在实例化子类时生成错误:Python抽象属性();Can';t使用抽象方法实例化抽象类[];,但我做到了,python,python-3.x,class,oop,abstract,Python,Python 3.x,Class,Oop,Abstract,我试图在Python3.7中创建一个基类,其中包含许多抽象python属性 我用@property、@abstractmethod、@property.setter注释尝试了一种方法(请参见下面的“开始”)。这是可行的,但如果子类未实现setter,则不会引发异常。对我来说,这就是使用@abstract的意义所在,所以这是不好的 所以我尝试了另一种方法(见下面的“end”),使用两个@abstractmethod方法和一个“property()”,它本身不是抽象的,而是使用这些方法。此方法在实例
# {TypeError}Can't instantiate abstract class FirstStep with abstract methods end
我很清楚地实现了抽象方法,所以我不明白它的意思。“end”属性没有标记为@abstract,但是如果我注释掉它,它会运行(但是我没有得到我的属性)。我还添加了测试非抽象方法“test_expressed_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
我怀疑这是抽象方法和属性交互中的一个bug 在基类中,按顺序发生以下事情:
start
的抽象方法start
现在引用此属性,唯一引用的原始名称现在由Self.start.fget
持有start.setter
的临时引用,因为名称start
即将绑定到另一个对象start
start
的属性。此属性的getter是来自1的方法,setter是来自4的方法。现在start
引用此属性<代码>开始。fget指1)中的方法start.fset
指的是4)中的方法属性的定义将其标记为抽象属性。因为它的所有组件方法都是抽象的。更重要的是,您在步骤中有以下条目
开始
,属性
结束
,属性
set\u end
,用于end
gen\u end
,是end
请注意,start
属性的组件函数丢失,因为\uuuu abstractmethods\uuuu
存储需要重写的对象的名称,而不是引用。使用property
和结果属性的setter
方法作为装饰器重复替换名称start
所指的内容
现在,在您的子类中,您定义了一个名为start
的新属性,隐藏继承的属性,该属性没有setter,也没有具体的方法作为其getter。此时,您是否为此属性提供setter无关紧要,因为就abc
机器而言,您已经提供了它所要求的一切:
名称start
名称的具体方法get\u end
和set\u end
隐式地为名称end
提供了一个具体的定义,因为属性end
的所有底层函数都提供了具体的定义
@切普纳回答并解释得很好。基于此,我想出了一个解决办法,那就是。。。好。。。由你决定。最好是偷偷摸摸。但它实现了我的3个主要目标:
为子类中未实现的setter引发异常
支持python属性语义(vs.函数等)
避免样板文件重新声明每个子类中仍然可能无法解决的#1的每个属性
只需在基类(而不是属性)中声明抽象get/set函数。然后将@classmethod初始值设定项添加到基类中,该基类使用这些抽象方法创建实际属性,但此时,它们将是子类上的具体方法
在初始化属性的子类声明之后是一行。没有什么能强制执行这个命令,所以它不是铁的。在这个例子中并没有大的节省,但是我有很多属性。最终的结果并不像我想象的那么糟糕。希望听到我忽略的事情的评论或警告
from abc import abstractmethod, ABC
class ParentTask(object):
def __init__(self):
self.first_step = FirstStep(self)
self.second_step = SecondStep(self)
print(self.first_step.end)
print(self.second_step.end)
class Step(ABC):
def __init__(self, task):
self.task = task
@classmethod
def init_properties(cls):
cls.end = property(cls.get_end, cls.set_end)
@abstractmethod
def get_end(self):
pass
@abstractmethod
def set_end(self, value):
pass
class FirstStep(Step):
def get_end(self):
return 1
def set_end(self, value):
self.task.end = value
class SecondStep(Step):
def get_end(self):
return 2
def set_end(self, value):
self.task.end = value
FirstStep.init_properties()
SecondStep.init_properties()
ParentTask()
我无法通过3.7.4或3.8.5上的start
重现您的问题。我得到TypeError:无法用抽象方法start实例化抽象类FirstStep
。这是从3.3开始的。哦,我明白了。您定义了一个getter而没有setter,并且希望getter和setter都是必需的。别理我。是的@chepner回答似乎解释了这一部分。剩下的我还在努力消化。用@abstractmethod
+@property
记录替换方法。给定的示例明确指出,属性可以是部分抽象的(例如,在其示例中,只有setter而不是getter),并且您应该只覆盖属性的该部分(这意味着如果您有多个抽象组件,则必须覆盖所有抽象组件),事实上,它实际上并没有这样做,这肯定构成了一个bug。显然,有。看起来我在那里读到的暗示并不真实;可以通过引用父属性覆盖单个图元,但如果