Python 中的抽象属性[重复]

2025-03-05 09:14:00
admin
原创
102
摘要:问题描述:在 Python 中使用抽象属性实现以下 Scala 代码的最短/最优雅的方法是什么?abstract class Controller { val path: String } Scala 编译器强制 的子类Controller定义“路径”。子类如下所示:class MyControl...

问题描述:

在 Python 中使用抽象属性实现以下 Scala 代码的最短/最优雅的方法是什么?

abstract class Controller {

    val path: String

}

Scala 编译器强制 的子类Controller定义“路径”。子类如下所示:

class MyController extends Controller {

    override val path = "/home"

}

解决方案 1:

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

如果在派生类中声明a或失败,将引发如下错误:b`B`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

解决方案 2:

自从最初提出这个问题以来,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访问时抛出错误,从而使执行不那么严格。

这并不是对原始答案的指责。自发布以来,抽象类语法已发生重大变化,在这种情况下,允许更简洁、更实用的实现。

解决方案 3:

Python 对此有一个内置异常,但您直到运行时才会遇到该异常。

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


class SubClass(Base):
    path = 'blah'

解决方案 4:

在 Python 3.6+ 中,您可以注释抽象类(或任何变量)的属性,而无需为该属性提供值。

from abc import ABC

class Controller(ABC):
    path: str

class MyController(Controller):
    def __init__(self, path: str):
        self.path = path

这使得代码非常干净,其中明显属性是抽象的。

需要注意的是,如果子类未提供实现,则在定义时不会引发异常。但是,AttributeError如果有任何东西试图访问未定义的属性,则会引发异常。

解决方案 5:

您可以在abc.ABC抽象基类中创建一个具有如下值的属性NotImplemented,这样如果该属性未被覆盖然后被使用,则会在运行时显示一个表达意图的明确错误。

以下代码使用PEP 484类型提示来帮助 PyCharm 正确地静态分析属性的类型path

from abc import ABC

class Controller(ABC):
    path: str = NotImplemented

class MyController(Controller):
    path = "/home"

解决方案 6:

从 Python 3.6 开始,你可以__init_subclass__在初始化时使用它来检查子类的类变量:

from abc import ABC

class A(ABC):
    @classmethod
    def __init_subclass__(cls):
        required_class_variables = [
            'foo',
            'bar',
        ]
        for var in required_class_variables:
            if not hasattr(cls, var):
                raise NotImplementedError(
                    f'Class {cls} lacks required `{var}` class attribute'
                )

如果未定义缺失的类变量,则会在初始化子类时引发错误,因此您不必等到访问缺失的类变量。

解决方案 7:

对于Python 3.3以上版本,有一个优雅的解决方案

from abc import ABC, abstractmethod
    
class BaseController(ABC):
    @property
    @abstractmethod
    def path(self) -> str:
        ...

class Controller(BaseController):
    path = "/home"


# Instead of an elipsis, you can add a docstring for clarity
class AnotherBaseController(ABC):
    @property
    @abstractmethod
    def path(self) -> str:
        """
        :return: the url path of this controller
        """

尽管已经给出了一些很好的答案,但我认为这个答案仍然会增加一些价值。这种方法有两个优点:

  1. ...在抽象方法体中比 更可取pass。与 不同pass...表示没有操作pass仅表示没有实际实现

  2. ...比 throwing 更推荐NotImplementedError(...)。如果子类中缺少抽象字段的实现,这会自动提示极其详细的错误。相比之下,NotImplementedError它本身并没有说明为什么缺少实现。此外,它需要人工来实际引发它。

解决方案 8:

我稍微修改了一下@James 的回答,这样那些装饰器就不会占用太多空间了。如果你有多个这样的抽象属性需要定义,那么这很方便:

from abc import ABC, abstractmethod

def abstractproperty(func):
   return property(classmethod(abstractmethod(func)))

class Base(ABC):

    @abstractproperty
    def CONSTANT(cls): ...

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


class Derived(Base):
    CONSTANT = 42

class BadDerived(Base):
    BAD_CONSTANT = 42

Derived()       # -> Fine
BadDerived()    # -> Error

解决方案 9:

Python3.6 的实现可能如下所示:

In [20]: class X:
    ...:     def __init_subclass__(cls):
    ...:         if not hasattr(cls, 'required'):
    ...:             raise NotImplementedError

In [21]: class Y(X):
    ...:     required = 5
    ...:     

In [22]: Y()
Out[22]: <__main__.Y at 0x7f08408c9a20>

解决方案 10:

您的基类可以实现一种__new__检查类属性的方法:

class Controller(object):
    def __new__(cls, *args, **kargs):
        if not hasattr(cls,'path'): 
            raise NotImplementedError("'Controller' subclasses should have a 'path' attribute")
        return object.__new__(cls)

class C1(Controller):
    path = 42

class C2(Controller):
    pass


c1 = C1() 
# ok

c2 = C2()  
# NotImplementedError: 'Controller' subclasses should have a 'path' attribute

这样,实例化时就会引发错误

解决方案 11:

class AbstractStuff:
    @property
    @abc.abstractmethod
    def some_property(self):
        pass

我认为从 3.3 开始abc.abstractproperty它已被弃用。

解决方案 12:

Bastien Léonard 的回答提到了抽象基类模块,而 Brendan Abel 的回答则涉及未实现的属性引发的错误。为了确保类未在模块之外实现,您可以在基类名称前加上下划线,表示它是模块私有的(即未导入)。

IE

class _Controller(object):
    path = '' # There are better ways to declare attributes - see other answers

class MyController(_Controller):
    path = '/Home'

解决方案 13:

看一下 abc(抽象基类)模块:http://docs.python.org/library/abc.html

然而,我认为最简单和最常见的解决方案是在创建基类的实例或访问其属性时引发异常。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3975  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2742  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   80  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   88  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   77  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用