定义抽象实例属性(而不是属性)的最佳实践是什么?
我想写这样的东西:
class AbstractFoo(metaclass=ABCMeta):
@property
@abstractmethod
def bar(self):
pass
class Foo(AbstractFoo):
def __init__(self):
self.bar = 3而不是:
class Foo(AbstractFoo):
def __init__(self):
self._bar = 3
@property
def bar(self):
return self._bar
@bar.setter
def setbar(self, bar):
self._bar = bar
@bar.deleter
def delbar(self):
del self._bar属性很方便,但是对于不需要计算的简单属性,它们是过头的。这对于将由用户子类和实现的抽象类尤其重要(我不想强迫某个人使用@property,因为他本来可以在__init__中编写self.foo = foo )。
Python中的抽象属性问题是使用@property和@abstractmethod的唯一答案:它没有回答我的问题。
通过ActiveState实现抽象类属性的AbstractAttribute方法可能是正确的,但我不确定。它也只适用于类属性,而不适用于实例属性。
发布于 2014-05-23 15:24:54
如果您确实希望强制子类定义给定的属性,则可以使用元类:
class AbstractFooMeta(type):
def __call__(cls, *args, **kwargs):
"""Called when you call Foo(*args, **kwargs) """
obj = type.__call__(cls, *args, **kwargs)
obj.check_bar()
return obj
class AbstractFoo(object):
__metaclass__ = AbstractFooMeta
bar = None
def check_bar(self):
if self.bar is None:
raise NotImplementedError('Subclasses must define bar')
class GoodFoo(AbstractFoo):
def __init__(self):
self.bar = 3
class BadFoo(AbstractFoo):
def __init__(self):
pass基本上,元类重新定义__call__,以确保在实例上的init之后调用check_bar。
GoodFoo() # ok
BadFoo () # yield NotImplementedError发布于 2018-05-16 22:57:35
与公认的答案相比,这可能是一个更好的解决方案:
from better_abc import ABCMeta, abstract_attribute # see below
class AbstractFoo(metaclass=ABCMeta):
@abstract_attribute
def bar(self):
pass
class Foo(AbstractFoo):
def __init__(self):
self.bar = 3
class BadFoo(AbstractFoo):
def __init__(self):
pass它的行为会是这样:
Foo() # ok
BadFoo() # will raise: NotImplementedError: Can't instantiate abstract class BadFoo
# with abstract attributes: bar这个答案使用了与接受的答案相同的方法,但是与内置的ABC很好地集成,并且不需要check_bar()助手的样板。
以下是better_abc.py内容:
from abc import ABCMeta as NativeABCMeta
class DummyAttribute:
pass
def abstract_attribute(obj=None):
if obj is None:
obj = DummyAttribute()
obj.__is_abstract_attribute__ = True
return obj
class ABCMeta(NativeABCMeta):
def __call__(cls, *args, **kwargs):
instance = NativeABCMeta.__call__(cls, *args, **kwargs)
abstract_attributes = {
name
for name in dir(instance)
if getattr(getattr(instance, name), '__is_abstract_attribute__', False)
}
if abstract_attributes:
raise NotImplementedError(
"Can't instantiate abstract class {} with"
" abstract attributes: {}".format(
cls.__name__,
', '.join(abstract_attributes)
)
)
return instance好的是你能做到:
class AbstractFoo(metaclass=ABCMeta):
bar = abstract_attribute()它的工作原理和上面一样。
还可以使用:
class ABC(ABCMeta):
pass若要定义自定义ABC助手,请执行以下操作。PS。我认为这个代码是CC0。
这可以通过使用AST解析器来通过扫描__init__代码来更早地(在类声明上)来改进,但目前看来这似乎是过分的(除非有人愿意实现)。
2021:打字支助
您可以使用:
from typing import cast, Any, Callable, TypeVar
R = TypeVar('R')
def abstract_attribute(obj: Callable[[Any], R] = None) -> R:
_obj = cast(Any, obj)
if obj is None:
_obj = DummyAttribute()
_obj.__is_abstract_attribute__ = True
return cast(R, _obj)这将让我突出一些打字问题。
class AbstractFooTyped(metaclass=ABCMeta):
@abstract_attribute
def bar(self) -> int:
pass
class FooTyped(AbstractFooTyped):
def __init__(self):
# skipping assignment (which is required!) to demonstrate
# that it works independent of when the assignment is made
pass
f_typed = FooTyped()
_ = f_typed.bar + 'test' # Mypy: Unsupported operand types for + ("int" and "str")
FooTyped.bar = 'test' # Mypy: Incompatible types in assignment (expression has type "str", variable has type "int")
FooTyped.bar + 'test' # Mypy: Unsupported operand types for + ("int" and "str")对于缩写符号,如@SMiller在评论中所建议的:
class AbstractFooTypedShorthand(metaclass=ABCMeta):
bar: int = abstract_attribute()
AbstractFooTypedShorthand.bar += 'test' # Mypy: Unsupported operand types for + ("int" and "str")发布于 2017-03-01 10:52:23
仅仅因为在抽象基类上将其定义为abstractproperty并不意味着您必须在子类上创建一个属性。
例如,你可以:
In [1]: from abc import ABCMeta, abstractproperty
In [2]: class X(metaclass=ABCMeta):
...: @abstractproperty
...: def required(self):
...: raise NotImplementedError
...:
In [3]: class Y(X):
...: required = True
...:
In [4]: Y()
Out[4]: <__main__.Y at 0x10ae0d390>如果要在__init__中初始化值,可以这样做:
In [5]: class Z(X):
...: required = None
...: def __init__(self, value):
...: self.required = value
...:
In [6]: Z(value=3)
Out[6]: <__main__.Z at 0x10ae15a20>因为Python3.3中的abstractproperty是已弃用。因此Python 3用户应该使用以下内容:
from abc import ABCMeta, abstractmethod
class X(metaclass=ABCMeta):
@property
@abstractmethod
def required(self):
raise NotImplementedErrorhttps://stackoverflow.com/questions/23831510
复制相似问题