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

使用dataclass时子类重写父类属性setter触发AttributeError

解决dataclass子类重写父类属性setter的问题

你用dataclass定义了基类SimpleUrl,想在子类FullUrl中重写url属性的setter,但运行代码时触发AttributeError: type object 'SimpleUrl' has no attribute 'url'错误。

原代码

from dataclasses import dataclass, field

@dataclass(repr=False, eq=False)
class SimpleUrl:

    id: int  # pylint: disable=invalid-name
    url: str

    def __eq__(self, ins: object) -> bool:
        return isinstance(ins, SimpleUrl) and (self.url == ins.url)

    def __iter__(self) -> iter:
        return iter((self.id, self.url))


@dataclass(repr=False, eq=False)
class FullUrl(SimpleUrl):

    ssl: bool = field(init=False, default=None)

    @SimpleUrl.url.setter
    def url(self, url: str) -> None:
        self.url = url
        self.ssl = url.startswith('https://')

fu = FullUrl('https://www.google.com')
print(fu.ssl)

错误信息

Exception has occurred: AttributeError
type object 'SimpleUrl' has no attribute 'url'
  File "D:\projects\tests\test_urls.py", line 22, in FullUrl
    @SimpleUrl.url.setter
     ^^^^^^^^^^^^^
  File "D:\projects\tests\test_urls.py", line 18, in <module>
    class FullUrl(SimpleUrl):
AttributeError: type object 'SimpleUrl' has no attribute 'url'

错误原因

  1. dataclass自动生成的属性是实例属性,不是类级别的property,所以SimpleUrl类本身没有url这个属性,无法通过@SimpleUrl.url.setter语法重写。
  2. 即使能定义setter,直接写self.url = url会触发无限递归——赋值操作会再次调用当前setter方法。

解决方案

方案一:父类显式封装url为property

修改父类,将url通过property封装,用私有字段存储实际值,子类可安全重写setter:

from dataclasses import dataclass, field

@dataclass(repr=False, eq=False)
class SimpleUrl:
    id: int  # pylint: disable=invalid-name
    url: str

    def __post_init__(self):
        # 将dataclass初始化的url值存入私有字段
        self._url = self.url
        # 删除自动生成的实例属性,让property生效
        del self.url

    @property
    def url(self) -> str:
        return self._url

    @url.setter
    def url(self, value: str) -> None:
        self._url = value

    def __eq__(self, ins: object) -> bool:
        return isinstance(ins, SimpleUrl) and (self.url == ins.url)

    def __iter__(self) -> iter:
        return iter((self.id, self.url))


@dataclass(repr=False, eq=False)
class FullUrl(SimpleUrl):
    ssl: bool = field(init=False, default=None)

    def __post_init__(self):
        super().__post_init__()
        # 初始化时设置ssl状态
        self.ssl = self.url.startswith('https://')

    @SimpleUrl.url.setter
    def url(self, url: str) -> None:
        # 调用父类setter设置核心值
        super().url = url
        # 更新ssl状态
        self.ssl = url.startswith('https://')

# 正确实例化(传入id和url两个参数)
fu = FullUrl(1, 'https://www.google.com')
print(fu.ssl)  # 输出True

# 修改url时自动更新ssl
fu.url = 'http://example.com'
print(fu.ssl)  # 输出False

方案二:子类独立封装url属性(不修改父类)

如果不想改动父类,子类可自行定义url的property,覆盖父类的实例属性:

from dataclasses import dataclass, field

@dataclass(repr=False, eq=False)
class SimpleUrl:
    id: int  # pylint: disable=invalid-name
    url: str

    def __eq__(self, ins: object) -> bool:
        return isinstance(ins, SimpleUrl) and (self.url == ins.url)

    def __iter__(self) -> iter:
        return iter((self.id, self.url))


@dataclass(repr=False, eq=False)
class FullUrl(SimpleUrl):
    ssl: bool = field(init=False, default=None)
    # 子类私有字段存储url值
    _url: str = field(init=False)

    def __post_init__(self):
        # 将父类初始化的url值存入子类私有字段
        self._url = self.url
        # 删除父类的实例属性,让子类property生效
        del self.url
        # 初始化ssl状态
        self.ssl = self._url.startswith('https://')

    @property
    def url(self) -> str:
        return self._url

    @url.setter
    def url(self, url: str) -> None:
        self._url = url
        self.ssl = url.startswith('https://')

# 正确实例化(传入id和url两个参数)
fu = FullUrl(1, 'https://www.google.com')
print(fu.ssl)  # 输出True

额外提示

原代码中FullUrl('https://www.google.com')会触发参数错误,因为父类SimpleUrl需要id和url两个必填参数,实例化时必须同时传入。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 02:33:19