Python中的抽象属性

161 投票
13 回答
180554 浏览
提问于 2025-04-15 22:09

在Python中,如何用最简洁、最优雅的方式实现以下的Scala代码,其中包含一个抽象属性?

abstract class Controller {

    val path: String

}

在Scala中,Controller的子类必须由编译器定义一个叫“path”的属性。一个子类可能是这样的:

class MyController extends Controller {

    override val path = "/home"

}

13 个回答

119

自从这个问题最初被提出来后,Python 在抽象类的实现方式上发生了变化。我使用了一种稍微不同的方法,利用了 Python 3.6 中的 abc.ABC 形式。在这里,我把常量定义为一个属性,这个属性必须在每个子类中定义。

from abc import ABC, abstractmethod


class Base(ABC):

    @classmethod
    @property
    @abstractmethod
    def CONSTANT(cls):
        raise NotImplementedError

    def print_constant(self):
        print(type(self).CONSTANT)


class Derived(Base):
    CONSTANT = 42

这样一来,派生类就必须定义这个常量,否则当你尝试创建子类的实例时,就会抛出一个 TypeError 错误。如果你想在抽象类中使用这个常量的任何功能,你必须通过 type(self).CONSTANT 来访问子类的常量,而不能直接用 CONSTANT,因为在基类中这个值是未定义的。

还有其他实现方式,但我觉得这种语法对读者来说最简单明了。

之前的回答都提到了有用的观点,但我觉得被接受的答案并没有直接回答问题,因为:

  • 问题是关于抽象类的实现,但被接受的答案没有遵循抽象的形式。
  • 问题要求强制实现。我认为这个答案的强制性更严格,因为如果 CONSTANT 没有定义,创建子类实例时会导致运行时错误。而被接受的答案允许对象被实例化,只有在访问 CONSTANT 时才会抛出错误,这样的强制性就没那么严格了。

这并不是要批评原来的答案。自从它们发布以来,抽象类的语法发生了重大变化,这在这种情况下允许更整洁和更实用的实现。

173

Python 3.3+

from abc import ABCMeta, abstractmethod


class A(metaclass=ABCMeta):
    def __init__(self):
        # ...
        pass

    @property
    @abstractmethod
    def a(self):
        pass

    @abstractmethod
    def b(self):
        pass


class B(A):
    a = 1

    def b(self):
        pass

如果在派生类 B 中没有声明 ab,就会出现一个错误,叫做 TypeError,错误信息可能是:

TypeError: 不能实例化抽象类 B,因为它有抽象方法 a

Python 2.7

在这个版本中,有一个叫做 @abstractproperty 的装饰器可以用来解决这个问题:

from abc import ABCMeta, abstractmethod, abstractproperty


class A:
    __metaclass__ = ABCMeta

    def __init__(self):
        # ...
        pass

    @abstractproperty
    def a(self):
        pass

    @abstractmethod
    def b(self):
        pass


class B(A):
    a = 1

    def b(self):
        pass
116

Python有一个内置的异常处理机制,不过你要等到程序运行时才会遇到这个异常。

class Base(object):
    @property
    def path(self):
        raise NotImplementedError


class SubClass(Base):
    path = 'blah'

撰写回答