【问题标题】:Why does the following code not throw an error?为什么下面的代码不会抛出错误?
【发布时间】:2014-06-19 13:23:49
【问题描述】:

我正在尝试使用 Python 3 编写一个抽象类,如下所示:

from abc import *


class Base(metaclass=ABCMeta):
    @abstractmethod
    def __init__(self, text=''):
        self._text = text

    @property
    @abstractmethod
    def text(self):
        return self._text

    @text.setter
    @abstractmethod
    def text(self, text):
        self._text = text


class SubClass(Base):

    def __init__(self, text):
        super().__init__(text)

    @property
    def text(self):
        return super().text

q = SubClass("Test")

当我运行文件时,解释器不会抱怨 text.setter 没有实现。为什么没有错误?

【问题讨论】:

  • 这是 Python 3 的哪个特定版本?
  • 你不是在 text.setter 的抽象声明中提供了带有self._text = text 的基本实现吗?抽象类的继承将在子类不覆盖它的情况下为子类提供基本实现,至少在大多数 OO 语言中它是这样工作的。
  • @user2357112 Python 3.4
  • @RyanJ:通常,所有带有abstractmethod 的东西都需要被覆盖以实例化一个类。例如,即使在 abstractmethod 装饰器下为 __init__ 提供了一个真实实现的函数,它仍然需要被覆盖。
  • 啊,是的。我跳到了 abc 库的 python 文档,确实是这样。我没有那么新的 python 版本,所以我无法测试。

标签: python python-3.x properties abstract-methods


【解决方案1】:

Base 类中text 属性的getter 和setter 都被命名为text,因此它们只在__abstractmethods__ 集中出现一次。当您覆盖 SubClass 中的 getter 时,它“计数”就好像它也覆盖了 setter。

不幸的是,虽然只有 getter 的抽象 property 可以正常工作,但似乎并没有一种优雅的方式来拥有一个同时具有抽象 getter 和 setter 的属性。如果您使用单个名称,则只需要覆盖该名称(并且正如您所发现的那样,覆盖不需要设置器)。如果您为这些函数使用单独的名称,然后使用text = property(_text_get, _text_set),则具体的子类将需要替换所有三件事(getter、setter 和属性对象本身)。更好的方法可能是让属性本身在 Base 类中具体化,但让它调用抽象的 getter 和 setter 实现函数,这些函数可以是抽象的,并且可以轻松覆盖哪些子类:

@abstractmethod
def _text_get_imp(self):
    return self._text

@abstractmethod
    _text_set_imp(self, value):
    self._text = value

@property
def text(self):
    return self._text_get_imp()

@text.setter
def text(self, value)
    self._text_set_imp(value)

编辑:今天阅读了(现已弃用)abc.abstractproperty 的文档后,我想我更好地理解了为什么只读属性没有错误(它并不那么简单正如我在上面所说的那样)。

您没有收到错误的原因是您的新属性具有与基类不同的“设置”实现。当然,这种行为是引发异常,但从技术上讲,这是一种覆盖原始 setter 行为的不同行为。

如果您通过使用@Base.text.getter 作为SubClass 中被覆盖的setter 函数的装饰器来更新旧属性,而不是完全从头开始创建新的property,您会收到关于抽象方法@ 的错误987654335@ 未被覆盖。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-09
    • 2017-07-08
    • 1970-01-01
    • 2015-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多