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

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

然后我创建了Base的一个子类,称为Base_1,它意味着提供一些功能,但仍然是抽象的。在Base_1中没有name属性,但是python在没有错误的情况下指示了该类的对象。如何创建抽象属性?

from abc import ABCMeta, abstractmethod


class Base(object):
__metaclass__ = ABCMeta
def __init__(self, strDirConfig):
self.strDirConfig = strDirConfig
    

@abstractmethod
def _doStuff(self, signals):
pass
    

@property
@abstractmethod
def name(self):
# this property will be supplied by the inheriting classes
# individually
pass
    



class Base_1(Base):
__metaclass__ = ABCMeta
# this class does not provide the name property, should raise an error
def __init__(self, strDirConfig):
super(Base_1, self).__init__(strDirConfig)
    

def _doStuff(self, signals):
print 'Base_1 does stuff'
        



class C(Base_1):
@property
def name(self):
return 'class C'
    

        

if __name__ == '__main__':
b1 = Base_1('abc')
143417 次浏览

Python 3.3之前,你不能嵌套@abstractmethod@property

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

from abc import ABCMeta, abstractmethod, abstractproperty


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

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

Traceback (most recent call last):
File "foo.py", line 36, in
b1 = Base_1('abc')
TypeError: Can't instantiate abstract class Base_1 with abstract methods name

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

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

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 ABC, abstractproperty


class C(ABC):
@abstractproperty
def my_abstract_property(self):
...

根据James的回答

def compatibleabstractproperty(func):


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

把它当装饰用

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

如果你想让所需的实例级属性也使用属性装饰器,在抽象类中使用@property装饰器(如詹姆斯的回答中推荐的那样)也是有效的。

如果你不想使用属性装饰器,你可以使用super()。我最终从数据类中使用了类似__post_init__()的东西,它获得了实例级属性所需的功能:

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.

python 3.6 +中,你也可以在不提供默认值的情况下注释变量。我发现这是一种更简洁的抽象方法。

class Base():
name: str
    

def print_name(self):
print(self.name)  # will raise an Attribute error at runtime if `name` isn't defined in subclass


class Base_1(Base):
name = "base one"

它也可以用来强制你在__new____init__方法中初始化变量

作为另一个例子,当你尝试初始化Base_1类时,下面的代码将失败

    class Base():
name: str


def __init__(self):
self.print_name()


class Base_1(Base):
_nemo = "base one"
    

b = Base_1()

AttributeError: 'Base_1' object has no attribute 'name'

例如,你可以用@abstractmethod@property@name.setter@name.deleterPerson抽象类中定义抽象的getter, setter和deleter,如下所示。*@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

然后,你可以用Student扩展Person抽象类,覆盖Student中的抽象getter、setter和删除器,实例化Student并调用getter、setter和删除器,如下所示:

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中的抽象setter和delete,并实例化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
# ...

出现以下错误:

没有定义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:属性对象的属性'isabstractmethod'不可写

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

一个最小的例子是这样的:

class BaseMetaClass(type):
def __new__(mcls, class_name, bases, attrs):
required_attrs = ('foo', 'bar')
for attr in required_attrs:
if not attr 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

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

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