如何在 python 抽象类中创建抽象属性?

2025-04-15 09:20:00
admin
原创
32
摘要:问题描述:在下面的代码中,我创建了一个基抽象类Base。我希望所有继承自的类都Base提供该name属性,所以我将此属性设为@abstractmethod。然后我创建了 的子类Base,名为Base_1,旨在提供一些功能,但仍然保持抽象。name中没有属性Base_1,但 Python 仍然能够毫无错误地实例...

问题描述:

在下面的代码中,我创建了一个基抽象类Base。我希望所有继承自的类都Base提供该name属性,所以我将此属性设为@abstractmethod

然后我创建了 的子类Base,名为Base_1,旨在提供一些功能,但仍然保持抽象。name中没有属性Base_1,但 Python 仍然能够毫无错误地实例化该类的一个对象。如何创建抽象属性?

from abc import ABCMeta, abstractmethod

class Base(object):
# class Base(metaclass = ABCMeta): <- Python 3
    __metaclass__ = ABCMeta
    def __init__(self, str_dir_config):
        self.str_dir_config = str_dir_config
    
    @abstractmethod
    def _do_stuff(self, signals):
        pass
    
    @property    
    @abstractmethod
    def name(self):
        """This property will be supplied by the inheriting classes
        individually.
        """
        pass
    

class Base1(Base):
    __metaclass__ = ABCMeta
    """This class does not provide the name property and should
    raise an error.
    """
    def __init__(self, str_dir_config):
        super(Base1, self).__init__(str_dir_config)
        # super().__init__(str_dir_config) <- Python 3
    
    def _do_stuff(self, signals):
        print "Base_1 does stuff"
        # print("Base_1 does stuff") <- Python 3

class C(Base1):
    @property
    def name(self):
        return "class C"
    

if __name__ == "__main__":
    b1 = Base1("abc")

解决方案 1:

自Python 3.3以来,一个错误被修复,这意味着property()当装饰器应用于抽象方法时,现在可以正确地将其识别为抽象的。

注意:顺序很重要,你必须使用@property上面的@abstractmethod

Python 3.3+:( python文档):

from abc import ABC, abstractmethod

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

Python 2:(python 文档)

from abc import ABCMeta, abstractproperty

class C:
    __metaclass__ = ABCMeta

    @abstractproperty
    def my_abstract_property(self):
        ...

解决方案 2:

在Python 3.3之前,不能嵌套@abstractmethodand @property

用于@abstractproperty创建抽象属性(文档)。

from abc import ABCMeta, abstractmethod, abstractproperty

class Base(object):
    # ...
    @abstractproperty
    def name(self):
        pass

代码现在引发了正确的异常:

回溯(最近一次调用最后一次):
  文件“foo.py”,第 36 行,位于
    b1 = Base_1('abc')  
TypeError:无法使用抽象方法名称实例化抽象类 Base_1

解决方案 3:

例如,您可以在抽象类@abstractmethod中使用和@property@name.setter来定义抽象的 getter、setter 和 deleter ,如下所示。 *必须是最内层的装饰器,否则会发生错误:@name.deleterPerson@abstractmethod

from abc import ABC, abstractmethod

class Person(ABC):

    @property
    @abstractmethod # The innermost decorator
    def name(self): # Abstract getter
        pass

    @name.setter
    @abstractmethod # The innermost decorator
    def name(self, name): # Abstract setter
        pass

    @name.deleter
    @abstractmethod # The innermost decorator
    def name(self): # Abstract deleter
        pass

然后,可以用类扩展Person抽象类,重写中的抽象getter,setter和deleter ,实例化并调用getter,setter和deleter,如下所示:StudentStudentStudent

class Student(Person):

    def __init__(self, name):
        self._name = name
    
    @property
    def name(self): # Overrides abstract getter
        return self._name
    
    @name.setter
    def name(self, name): # Overrides abstract setter
        self._name = name
    
    @name.deleter
    def name(self): # Overrides abstract deleter 
        del self._name

obj = Student("John") # Instantiates "Student" class
print(obj.name) # Getter
obj.name = "Tom" # Setter
print(obj.name) # Getter
del obj.name # Deleter
print(hasattr(obj, "name"))

输出:

John
Tom
False

实际上,即使您不重写Student中的抽象设置器和删除器并实例化Student,如下所示:

class Student(Person): # Extends "Person" class
    
    def __init__(self, name):
        self._name = name
    
    @property
    def name(self): # Overrides only abstract getter
        return self._name

    # @name.setter
    # def name(self, name): # Overrides abstract setter
    #     self._name = name
    
    # @name.deleter
    # def name(self): # Overrides abstract deleter 
    #     del self._name

obj = Student("John") # Instantiates "Student" class
# ...

没有出现错误如下图:

John
Tom
False

但是,如果您不重写Student中的抽象 getter、setter 和 deleter ,并且实例化Student,如下所示:

class Student(Person): # Extends "Person" class
    
    def __init__(self, name):
        self._name = name
    
    # @property
    # def name(self): # Overrides only abstract getter
    #     return self._name

    # @name.setter
    # def name(self, name): # Overrides abstract setter
    #     self._name = name
    
    # @name.deleter
    # def name(self): # Overrides abstract deleter 
    #     del self._name

obj = Student("John") # Instantiates "Student" class
# ...

出现以下错误:

类型错误:无法使用抽象方法名称实例化抽象类 Student

并且,如果您不重写Student中的抽象 getter并实例化Student,如下所示:

class Student(Person): # Extends "Person" class
    
    def __init__(self, name):
        self._name = name
    
    # @property
    # def name(self): # Overrides only abstract getter
    #     return self._name

    @name.setter
    def name(self, name): # Overrides abstract setter
        self._name = name
    
    @name.deleter
    def name(self): # Overrides abstract deleter 
        del self._name

obj = Student("John") # Instantiates "Student" class
# ...

出现以下错误:

NameError:名称“name”未定义

并且,如果@abstractmethod不是最内层的装饰器,如下所示:

from abc import ABC, abstractmethod

class Person(ABC):

    @abstractmethod # Not the innermost decorator
    @property
    def name(self): # Abstract getter
        pass

    @name.setter
    @abstractmethod # The innermost decorator
    def name(self, name): # Abstract setter
        pass

    @name.deleter
    @abstractmethod # The innermost decorator
    def name(self): # Abstract deleter
        pass

出现以下错误:

AttributeError:'property' 对象的属性 ' isabstractmethod ' 不可写

解决方案 4:

根据詹姆斯上述回答

def compatibleabstractproperty(func):

    if sys.version_info > (3, 3):             
        return property(abstractmethod(func))
    
    return abstractproperty(func)

并将其用作装饰器

@compatibleabstractproperty
def env(self):
    raise NotImplementedError()

解决方案 5:

如果您希望所需的实例级属性也使用属性装饰器,则使用抽象类中的装饰器(如 James 的回答中所建议的)是可行@property的。

如果您不想使用属性装饰器,可以使用super()。我最终使用了类似__post_init__()dataclasses 的方法,它获得了实例级属性所需的功能:

import abc
from typing import List

class Abstract(abc.ABC):
    """An ABC with required attributes.

    Attributes:
        attr0
        attr1 
    """

    @abc.abstractmethod
    def __init__(self):
        """Forces you to implement __init__ in 'Concrete'. 
        Make sure to call __post_init__() from inside 'Concrete'."""

    def __post_init__(self):
        self._has_required_attributes()
        # You can also type check here if you want.

    def _has_required_attributes(self):
        req_attrs: List[str] = ['attr0', 'attr1']
        for attr in req_attrs:
            if not hasattr(self, attr):
                raise AttributeError(f"Missing attribute: '{attr}'")

class Concrete(Abstract):

    def __init__(self, attr0, attr1):
        self.attr0 = attr0
        self.attr1 = attr1
        self.attr2 = "some value" # not required
        super().__post_init__() # Enforces the attribute requirement.

解决方案 6:

另一个可能的解决方案是使用元类。

最小示例如下所示:

class BaseMetaClass(type):
    def __new__(mcls, class_name, bases, attrs):
        required_attrs = ('foo', 'bar')
        for attr in required_attrs:
            if attr not in attrs:
                raise RuntimeError(f"You need to set {attr} in {class_name}")
        return super().__new__(mcls, class_name, bases, attrs)


class Base(metaclass=BaseMeta):
    foo: str
    bar: int

这种方法的一个优点是检查将在定义时进行(而不是实例化)。

此外,在子类中设置类属性比声明属性要容易一些(只要它们是预先知道的简单值),并且最终的类看起来会更简洁

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

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

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

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用