在一个类中实现复制方法的最佳实践是什么?

17

在类中实现copy方法的最佳做法是什么?

这里是我的类的一个示例:

class MyClass:
    def __init__(self, foo, bar=None):
        self.foo = foo
        if bar is not None:
            self.bar = bar
        else:
            self.bar = {'Hello': 'Ciao'}

我发现了一篇五年前的帖子,建议采用以下方法:

import copy

def copy(self):
    return MyClass(copy.copy(self.foo), copy.copy(self.bar))

现在这是唯一的方法,还是有其他可能性?

我需要创建一个对象的副本,以避免函数更改原始对象。该函数大致如下:

def translate_and_print(my_class, dict={'Hello':'Ciao'}):
    temp = my_class # here I want to use the copy method: temp = my_class.copy()
    temp.foo = temp.bar[temp.foo]
    print(temp.foo)
以下代码的输出是 "Ciao", "Ciao" ,但应该是 "Ciao", "Hello"。

以下代码的输出为 "Ciao"、"Ciao",但正确输出应为 "Ciao"、"Hello"。

mc = MyClass('Hello')
translate_and_print(mc)
print(mc.foo)

如果我使用copy()方法,会出现错误:

AttributeError: 'MyClass'对象没有属性'copy'


1
关于您的更新评论,我可以运行myobject = MyClass('foo'),然后执行copy.copy(myobject)而没有任何问题。您能否提供一个能够重现您错误的代码片段? - Jonas Adler
1
出于历史原因,保留上述评论。看起来你的错误是试图调用 my_class.copy(),而你应该调用 copy.copy(my_class) - Jonas Adler
1
我的帖子已经包括了,具体来说就是在结尾处可以简单地调用copy.copy(myobject) - Jonas Adler
3个回答

14
你应该实现__copy__方法,可能还需要实现__deepcopy__。文档说明如下:
为了使类定义自己的复制实现,它可以定义特殊方法__copy__()__deepcopy__()。前者被调用以实现浅复制操作;不传递任何其他参数。后者被调用以实现深度复制操作;它传递一个参数,即备忘录字典。如果__deepcopy__()实现需要对组件进行深度复制,则应使用组件作为第一个参数和备忘录字典作为第二个参数调用deepcopy()函数。
话虽如此,除非你做一些魔法(例如C分配或调用有状态的C库),否则你不需要自己实现__copy__,但是Python已经为你提供了这个功能,你只需调用copy.copy(myobject)即可。

2
谢谢您的建议,但这并没有解释如何实现该方法。 - paolof89
在你的例子中,没有理由实现__copy__。如果你有一个使用案例,实际上需要使用一些低级资源来要求你实现它,你能否更新你的例子,我们可以帮助解决这个问题?在你的例子中,你建议的实现没有添加任何东西,因为Python默认会这样做。 - Jonas Adler
我编辑了这个问题。 我的类中没有实现copy()方法。 - paolof89
结尾处的建议很好。我本来想创建复制方法,但其实不必要。copy.copy 就是一个万能解决方案。 - pauljohn32

8

你遇到了一个 AttributeError 错误,我认为这是因为你只创建了一个 copy 方法,而不是需要创建的 __copy__ 方法。

import copy

class MyClass:
    def __init__(self, name):
        self.name = name
    def __copy__(self):
        return MyClass(self.name)
    def __deepcopy__(self, memo):
        return MyClass(copy.deepcopy(self.name, memo))

__copy__ 方法用于浅拷贝,__deepcopy__ 方法用于深拷贝。

希望你明白了重点,这是我所参考的链接


这可能会导致子类中出现错误,因为隐式使用了 MyClass 的初始化程序。例如,子类实例的副本将变成 MyClass 的一个实例。 - Airat K

5

下面的实现只是复制,而不调用init()方法。

class MyClass(BaseClass):
    def __init__(self, name):
        super().__init__()
        self.name = name

    def __copy__(self):
        obj = type(self).__new__(self.__class__)
        obj.__dict__.update(self.__dict__)
        return obj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接