You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

抽象类子类调用父类抽象setter方法遇AttributeError问题求助

解决AttributeError: can't set the attribute问题

Hey, let's break down why you're running into this error and fix it properly.

问题根源

Your ValueHistorical subclass only implements the getter properties for db_ids and name, but completely misses their corresponding setter methods. The parent Indicator class defines these setters as abstract methods, which means your subclass must implement them to adhere to the abstract class contract. Without a setter defined, Python has no way to handle the assignment self.db_ids = [...] or self.name = ..., hence the AttributeError.

Also, while Python's ABC mechanism didn't block you from instantiating ValueHistorical (since you implemented the calculate method and getters), you technically didn't fully implement all abstract members of the parent class—this is a design oversight that leads to the runtime error.


解决方案一:复用父类校验逻辑的子类实现

We can extract the validation logic from the parent's setters into private helper methods, then have the subclass's setters call the parent's setter logic. This keeps your validation centralized while fulfilling the abstract method requirement:

from abc import ABC, abstractmethod
from typing import List

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

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

    # Centralized validation for db_ids
    def _validate_db_ids(self, ids: List[int]):
        assert isinstance(ids, list), "db_ids must be a list of integers"
        assert all(isinstance(id_, int) for id_ in ids), "All elements in db_ids must be integers"
        self._db_ids = ids

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

    @db_ids.setter
    @abstractmethod
    def db_ids(self, ids: List[int]):
        self._validate_db_ids(ids)

    # Centralized validation for name
    def _validate_name(self, set_name: str):
        assert isinstance(set_name, str), "name must be a string"
        self._name = set_name

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

    @name.setter
    @abstractmethod
    def name(self, set_name: str):
        self._validate_name(set_name)

class ValueHistorical(Indicator):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.kwargs = kwargs or {}
        self.db_ids = [119, 120, 121, 122]
        self.name = 'Value Based on Historical'

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

    # Implement db_ids setter, delegate to parent's logic
    @db_ids.setter
    def db_ids(self, ids: List[int]):
        super(ValueHistorical, self.__class__).db_ids.fset(self, ids)

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

    # Implement name setter, delegate to parent's logic
    @name.setter
    def name(self, set_name: str):
        super(ValueHistorical, self.__class__).name.fset(self, set_name)

    def calculate(self):
        pass

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

解决方案二:简化父类设计(通用getter)

If the getter logic is always just returning a private attribute (no customization needed in subclasses), we can make the parent's getters non-abstract. This way, your subclass only needs to implement the setters, which can directly reuse the parent's validation:

from abc import ABC, abstractmethod
from typing import List

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

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

    # Generic getter for db_ids (non-abstract)
    @property
    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

    # Generic getter for name (non-abstract)
    @property
    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):
        super().__init__(**kwargs)
        self.kwargs = kwargs or {}
        self.db_ids = [119, 120, 121, 122]
        self.name = 'Value Based on Historical'

    # Implement db_ids setter, reuse parent validation
    @db_ids.setter
    def db_ids(self, ids: List[int]):
        super(ValueHistorical, self).db_ids.__set__(self, ids)

    # Implement name setter, reuse parent validation
    @name.setter
    def name(self, set_name: str):
        super(ValueHistorical, self).name.__set__(self, set_name)

    def calculate(self):
        pass

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

为什么原始代码失败?

In your original code, ValueHistorical only defines @property (getter) for db_ids and name, but no @xxx.setter methods. When you try to assign values to these properties, Python looks for a setter method to handle the assignment—but since none exists (you didn't implement it, and the parent's abstract setter isn't bound to the subclass), it throws the AttributeError: can't set the attribute.

内容的提问来源于stack exchange,提问作者naz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:19:05