Python抽象属性();Can';t使用抽象方法实例化抽象类[];,但我做到了

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()”,它本身不是抽象的,而是使用这些方法。此方法在实例

我试图在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
    的抽象方法
  • 创建一个新属性,该属性使用1)中的抽象方法作为其getter。名称
    start
    现在引用此属性,唯一引用的原始名称现在由
    Self.start.fget
    持有
  • Python保存对
    start.setter
    的临时引用,因为名称
    start
    即将绑定到另一个对象
  • 您创建了第二个名为
    start
  • 3)中的引用使用了4)中的抽象方法来定义新属性,以替换当前绑定到名称
    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。显然,有。看起来我在那里读到的暗示并不真实;可以通过引用父属性覆盖单个图元,但如果