首页 > 解决方案 > 能够在抽象类的子类中设置属性

问题描述

我已经为此奋斗了半个小时,所以我已经通过了自己尝试半个小时的规则,并寻求您的帮助。我试图让 Child 使用抽象类的 setter 抽象方法,但它不起作用......

#!/usr/bin/env python3
from abc import ABC, abstractmethod
from typing import List

class Indicator(ABC):
    def __init__(self, **kwargs):
        super().__init__()
        pass

    @abstractmethod
    def calculate(self):
        """
        kwargs in children will most likely be date_from, date_to, index
        """
        raise NotImplementedError("The calculate method is not implemented!")

    @property
    @abstractmethod
    def db_ids(self):
        return self._db_ids

    @db_ids.setter
    @abstractmethod
    def db_ids(self, ids: List[int]):
        assert isinstance(ids, list)
        assert all(isinstance(id_, int) for id_ in ids)
        self._db_ids = ids

    @property
    @abstractmethod
    def name(self):
        return self._name

    @name.setter
    @abstractmethod
    def name(self, set_name: str):
        assert isinstance(set_name, str)
        self._name = set_name

# …………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………


class ValueHistorical(Indicator):
    def __init__(self, **kwargs):
        if kwargs:
            self.kwargs = kwargs
            super(ValueHistorical, self).__init__(**kwargs)

        self.db_ids = [119, 120, 121, 122]
        self.name = 'Value Based on Historical'

    @property
    def db_ids(self):
        return self._db_ids

    @property
    def name(self):
        return self._name

    def calculate(self):
        pass

ValueHistorical(**{'date_from': '2010-01-01', 'date_to': '2012-01-01'})

这里的论点无关紧要。我得到的错误是AttributeError: can't set the attribute'.

我想要实现的是在 ValueHistorical 构造函数内部,它转到它的父抽象类的 db_ids 设置器和分配它们时的名称。

标签: pythonpython-3.xabstract-class

解决方案


这实际上与 ABC 无关,但与您在子类中反弹属性但没有设置器的事实有关。这:

class ValueHistorical(Indicator):

    @property
    def db_ids(self):
        return self._db_ids

    @property
    def name(self):
        return self._name

只需将父属性替换为新属性,但将这些属性定义为只读,因为您没有提供设置器。

请记住,装饰器语法只是语法糖,所以:

@property
def getter(...): pass

只是一种更花哨的写作方式

def getter(...): pass
getter = property(getter)

由于 getter 和 setter 是property实例的属性,所以在子类中重新定义属性时,不能只重新定义 getter,还必须重新定义 setter。

这里的一个常见模式是让 getter 和 setter(如果有的话)委托给另一种方法,因此您不必重新实现整个事情,即:

class Base(object):

    @property
    def foo(self):
        return self._get_foo()

    @foo.setter
    def foo(self, value):
        self._set_foo(value)

    def _get_foo(self):
        # ...

    def _set_foo(self, value):
        # ...

因此子类可以覆盖_get_foo和/或_set_foo不必重新定义属性。

此外,同时应用propertyabstractmethod到一个函数是完全没用的。这:

@property
@abstractmethod
def db_ids(self):
    return self._db_ids

相当于

def db_ids(self):
    return self._db_ids

db_ids = property(abstractmethod(db_ids))

所以 ABC 将在这里看到的是属性 - 它的 getter(和/或 setter)已被装饰的事实abstractmethod被忽略,ABC 不会检查属性的 getter 和 setter。如果你把它们反过来,即

db_ids = abstractmethod(property(db_ids))

那么你根本就没有定义一个属性(实际上,它根本不起作用——你会从一开始就得到一个异常,“'property'对象没有属性' isabstractmethod '”)

FWIW,abstractmethod装饰器仅用于未定义的方法(空体),因此子类必须实现它们。如果您有默认实现,请不要将其标记为抽象,否则为什么要提供默认实现?

编辑:

您在评论中提到(关于已删除的答案):

我基本上希望 ValueHistorical 在 ValueHistorical 构造函数中分配 db_ids 和 name 时转到 Abstract 类的 setter 方法

然后最简单的解决方案就是我上面解释的那个:为 getter 和/或 setter 定义实现方法(您可以根据需要将它们中的任何一个或两者都抽象化)并使用具体属性来调用这些实现方法。

哦,是的:assert是一个开发工具,不要将它用于生产代码中的类型检查。如果你真的想做类型检查(这有时是有意义的,但通常不是完全浪费时间),使用isinstance并提出一个TypeError. 例如,您的db_idssetter 应如下所示:

    if not isinstance(ids, list):
        raise TypeError("ids should be a list")

    if not all(isinstance(id_, int) for id_ in ids)
        raise TypeError("ids items should be ints")

甚至更好:

    # you don't care if it really was a list actually, 
    # as long as you can build a list out of it, and
    # you don't care if it really contains ints as long
    # as you can build ints out of them.
    #
    # No need for typecheck here, if `ids` is not iterable
    # or what it yields cannot be used to build an int, 
    # this will raise, with way enough informations to
    # debug the caller.

    ids = [int(id) for id in ids)]

推荐阅读