python - 抽象属性(不是属性)?

标签 python abstract

定义抽象实例属性而不是属性的最佳实践是什么?

我想写这样的东西:

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 ,因为他本来可以编写 self.foo = foo __init__)。

Abstract attributes in Python问题建议作为使用 @property@abstractmethod 的唯一答案:它没有回答我的问题。

通过 AbstractAttribute 获得抽象类属性的 ActiveState 配方可能是正确的方法,但我不确定。它也只适用于类属性而不是实例属性。

最佳答案

与公认的答案相比,一个可能更好的解决方案:

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)

这将使 mypy 突出显示一些打字问题

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")

关于python - 抽象属性(不是属性)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23831510/

相关文章:

java - 处理特定项目的唯一 ID 的类 -> 是否抽象?

python - 在 Python 中保存从 fbprophet 生成的图

Python PostgreSQL 删除重复行

java - Java 抽象类和接口(interface)

delphi - Delphi中的抽象类

Java抽象方法签名

C# 抽象类实现

python - 参数 ?不使用 python postgres

python - 强制其危险的 URLSafeTimedSerializer 提供旧签名

python - Django Rest Framework SerializerMethodField 传递额外参数