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

为什么重写__getattr__或__getattribute__会导致deepcopy调用失败,如何解决?

报错原因

copy.deepcopy复制自定义类实例的流程中,会优先尝试读取实例的__dict__、__getstate__、__deepcopy__等特殊魔法属性来获取实例状态。你重写的__getattr__会在属性常规查找失败时触发,栈溢出的根因是无限递归:

  • deepcopy创建完新的空实例后,还未完成self.data属性的初始化,此时访问任何属性都会触发__getattr__
  • __getattr__的逻辑需要先访问self.data判断key是否存在,但此时self.data还没创建,常规查找失败,又会再次触发__getattr__,循环往复直到递归深度超出上限。

如果是重写__getattribute__触发的问题,原理更直接:__getattribute__会拦截所有属性访问请求,只要方法内部访问实例属性时没有通过super().__getattribute__调用父类原生逻辑,就会直接触发无限递归,哪怕是普通属性访问也会报错,和deepcopy没有必然关联。

修复方案

有两种常用的修复方式,可根据业务场景选择:

方案1:过滤魔法属性,不拦截特殊属性查找

修改__getattr__的逻辑,遇到双下划线开头和结尾的魔法属性直接抛出AttributeError,交给Python原生的属性查找逻辑处理,不走自定义的data字典判断。

class A:
    def __init__(self):
        self.data = {'key': 'value'}
    def __getattr__(self, key):
        # 放过所有魔法属性
        if key.startswith('__') and key.endswith('__'):
            raise AttributeError
        if key not in self.data:
            raise AttributeError
        return self.data[key]

该方案改造成本极低,适合只需要自定义普通属性查找逻辑的场景。

方案2:自定义__deepcopy__方法,主动控制复制逻辑

自己实现实例的深拷贝逻辑,避免deepcopy默认流程触发你的自定义属性查找规则。

import copy
class A:
    def __init__(self):
        self.data = {'key': 'value'}
    def __getattr__(self, key):
        if key not in self.data:
            raise AttributeError
        return self.data[key]
    def __deepcopy__(self, memo):
        new_obj = A()
        # 仅深拷贝自定义的data属性即可
        new_obj.data = copy.deepcopy(self.data, memo)
        # 将新实例加入memo字典,避免循环引用场景下的重复复制
        memo[id(self)] = new_obj
        return new_obj

该方案灵活度更高,适合实例复制有特殊业务要求的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 20:06:06