如何在Python中“重写”deepcopy?

4 投票
4 回答
4902 浏览
提问于 2025-04-15 23:56

我想要重写一个叫做 __deepcopy__ 的方法,用在一个用 SQLAlchemy 映射的类上,这样它就能忽略任何 SQLAlchemy 特有的属性,但能深拷贝类中的其他所有内容。

我对重写 Python 内置对象的知识不太多,但我大致知道我想要什么。

我们先来创建一个非常简单的类 User,这个类是用 SQLAlchemy 映射的。

class User(object):
    def __init__(self, user_id=None, name=None):
        self.user_id = user_id
        self.name = name

我用 dir() 函数查看了在映射之前和之后,SQLAlchemy 特有的属性有哪些,发现了 _sa_class_manager_sa_instance_state

问题
如果这些是唯一的属性,我该如何在定义 __deepcopy__ 时忽略它们呢?
另外,SQLAlchemy 是否会在映射的对象中注入其他属性?


(我在之前的问题中问过这个(在我选择了主要问题的答案后几天进行了编辑),但我觉得我错过了机会。对此我表示歉意。)


编辑 - 感谢 zifot 的回答修复了代码

我从 Python 文档中了解到,定义深拷贝时需要把 memo 作为一个额外的参数。经过一点小小的探索,我尝试了这个:

def __deepcopy__(self, memo):
    dpcpy = self.__class__()
    memo[id(self)] = dpcpy
    for attr in dir(self):
        if not attr.startswith('_'):
            value = getattr(self, attr)
            setattr(dpcpy, attr, copy.deepcopy(value, memo))
    return dpcpy

然后我创建了一个 User 的实例,如下:

snake = User(913, 'Snake,S.')  

之后,我尝试进行了一个 deepcopy 操作,如下:

snake_dc = copy.deepcopy(snake)

...结果 snake_dc 里还是有 SQLAlchemy 的属性...

我非常欢迎任何帮助、建议等。

4 个回答

0

我不是深拷贝方面的专家,但从这个错误来看,似乎你需要一个不带参数的构造函数,这样才能在调用 self.__class__() 时不传任何参数。

1

mavnn 说得对。比如,你可以尝试把User的初始化改成:

def __init__(self, user_id = None, name = None):
        self.user_id = user_id
        self.name = name

关于复制映射实例,我建议你看看 这个讨论

2

在进行(深)复制的时候,你不应该调用 __init__ 方法,而是应该调用 __new__ 方法。

下面是一个例子:

def __copy__(self):
    cls = self.__class__
    newobject = cls.__new__(cls)
    newobject.__dict__.update(self.__dict__)
    return newobject

撰写回答