抽象属性(不是属性)?

2024-09-29 00:18:35 发布

您现在位置:Python中文网/ 问答频道 /正文

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

我想写一些东西,比如:

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

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

通过^{}创建抽象类属性的ActiveState方法可能是正确的,但我不确定。它也只适用于类属性,而不适用于实例属性。


Tags: 实例self属性定义fooinitdefbar
3条回答

到了2018年,我们应该得到更好的解决方案:

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()helpers的样板。

以下是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助手。另外,我认为这个代码是CC0。

这可以通过使用AST解析器通过扫描__init__代码来提高早期(在类声明上)的性能,但目前看来这是一个过度的牺牲(除非有人愿意实现)。

仅仅因为在抽象基类上将其定义为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>

因为Python 3.3abstractpropertydeprecated。因此,Python 3用户应该使用以下内容:

from abc import ABCMeta, abstractmethod

class X(metaclass=ABCMeta):
    @property
    @abstractmethod
    def required(self):
        raise NotImplementedError

如果你真的想强制子类定义一个给定的属性,你可以使用元类。就我个人而言,我认为这可能是过度杀戮,不是很Python,但你可以这样做:

 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

相关问题 更多 >