Bor*_*lik 81 python abstract-class properties decorator
在下面的代码中,我创建了一个基本抽象类Base
.我希望所有继承的类都Base
提供name
属性,所以我创建了这个属性@abstractmethod
.
然后我创建了一个Base
名为的子类Base_1
,它旨在提供一些功能,但仍然是抽象的.没有name
属性Base_1
,但是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')
Run Code Online (Sandbox Code Playgroud)
Jam*_*mes 64
自Python 3.3以来,修复了一个错误,这意味着property()
当应用于抽象方法时,装饰器现在被正确识别为抽象.
从python文档:
class C(ABC):
@property
@abstractmethod
def my_abstract_property(self):
...
Run Code Online (Sandbox Code Playgroud)
cod*_*ape 42
在Python 3.3之前,你无法嵌套@abstractmethod
和@property
.
使用@abstractproperty
创建抽象属性(文档).
from abc import ABCMeta, abstractmethod, abstractproperty
class Base(object):
# ...
@abstractproperty
def name(self):
pass
Run Code Online (Sandbox Code Playgroud)
代码现在引发了正确的异常:
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
基于上面詹姆斯的回答
def compatibleabstractproperty(func):
if sys.version_info > (3, 3):
return property(abstractmethod(func))
else:
return abstractproperty(func)
Run Code Online (Sandbox Code Playgroud)
并将其用作装饰器
@compatibleabstractproperty
def env(self):
raise NotImplementedError()
Run Code Online (Sandbox Code Playgroud)
小智 6
如果您希望所需的实例级属性也使用属性@property
装饰器,则在抽象类中使用装饰器(按照James 的答案中的建议)是可行的。
如果您不想使用属性装饰器,可以使用super()
. 我最终使用了类似__post_init__()
from 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.
Run Code Online (Sandbox Code Playgroud)
在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"
Run Code Online (Sandbox Code Playgroud)
它还可用于强制您初始化__new__
或__init__
方法中的变量
作为另一个示例,当您尝试初始化类时,以下代码将Base_1
失败
class Base():
name: str
def __init__(self):
self.print_name()
class Base_1(Base):
_nemo = "base one"
b = Base_1()
Run Code Online (Sandbox Code Playgroud)
AttributeError: 'Base_1' object has no attribute 'name'
小智 5
例如,您可以使用@abstractmethod
and@property
或在抽象类@name.setter
中定义抽象 getter、setter 和 deleter ,如下所示。*必须是最里面的装饰器,否则会出现错误:@name.deleter
Person
@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
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用class扩展Person
抽象类,重写class中的抽象 getter、setter 和 deleter ,实例化类并调用 getter、setter 和 deleter,如下所示:Student
Student
Student
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"))
Run Code Online (Sandbox Code Playgroud)
输出:
John
Tom
False
Run Code Online (Sandbox Code Playgroud)
实际上,即使您不重写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
# ...
Run Code Online (Sandbox Code Playgroud)
没有出现错误如下图:
John
Tom
False
Run Code Online (Sandbox Code Playgroud)
但是,如果您不重写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
# ...
Run Code Online (Sandbox Code Playgroud)
出现以下错误:
类型错误:无法使用抽象方法名称实例化抽象类 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
# ...
Run Code Online (Sandbox Code Playgroud)
出现以下错误:
NameError:名称“名称”未定义
并且,if@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
Run Code Online (Sandbox Code Playgroud)
出现以下错误:
AttributeError:“property”对象的属性“ isabstractmethod ”不可写
归档时间: |
|
查看次数: |
49519 次 |
最近记录: |