ZetCode

Python __copy__ 方法

最后修改于 2025 年 4 月 8 日

这个全面的指南探讨了 Python 的 __copy__ 方法,这是一个控制浅拷贝行为的特殊方法。我们将介绍基本用法、自定义实现和实际示例。

基本定义

__copy__ 方法定义了当调用 copy.copy() 函数时,对象应该如何被复制。它返回对象的浅拷贝。

关键特征:它不接受任何参数(除了 self),应该返回一个新对象,并且由 copy.copy() 调用。对于深拷贝,请改用实现 __deepcopy__

基本的 __copy__ 实现

这是一个简单的实现,展示了 __copy__ 如何与基本对象一起工作。它演示了默认行为和自定义。

basic_copy.py
import copy

class Box:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def __copy__(self):
        print("__copy__ called")
        return Box(self.width, self.height)
    
    def __repr__(self):
        return f"Box({self.width}, {self.height})"

original = Box(10, 20)
copy_obj = copy.copy(original)
print(copy_obj)

这个例子展示了一个基本的 __copy__ 实现。当 copy.copy() 被调用时,它会调用 __copy__,后者创建一个具有相同尺寸的新 Box。

输出将显示“__copy__ called”并打印复制的 Box。 两个对象都是独立的实例,具有相同的属性值。

浅拷贝 vs 深拷贝

这个例子演示了在处理嵌套对象时浅拷贝和深拷贝之间的区别,以及 __copy__ 的作用。

shallow_deep.py
import copy

class Container:
    def __init__(self, items):
        self.items = items
    
    def __copy__(self):
        print("Shallow copy")
        return Container(self.items)
    
    def __deepcopy__(self, memo):
        print("Deep copy")
        return Container(copy.deepcopy(self.items, memo))

original = Container([[1, 2], [3, 4]])
shallow = copy.copy(original)
deep = copy.deepcopy(original)

original.items[0][0] = 99
print("Shallow:", shallow.items)  # Affected by change
print("Deep:", deep.items)       # Unaffected

这个例子展示了浅拷贝如何共享对嵌套对象的引用,而深拷贝则创建完全独立的副本。__copy__ 方法处理浅拷贝的情况。

在修改原始嵌套列表后,浅拷贝反映了更改,而深拷贝保持不变。 这演示了引用共享。

自定义复制行为

可以自定义 __copy__ 来精确控制复制的内容和方式。此示例显示了选择性属性复制。

custom_copy.py
import copy

class Account:
    def __init__(self, balance, transactions):
        self.balance = balance
        self.transactions = transactions
        self.last_access = None
    
    def __copy__(self):
        print("Creating account copy")
        new_account = Account(self.balance, self.transactions.copy())
        new_account.last_access = "Copied from original"
        return new_account

original = Account(1000, ["deposit 500", "withdraw 200"])
copy_acc = copy.copy(original)

original.balance = 1500
original.transactions.append("withdraw 300")
print("Original:", original.balance, original.transactions)
print("Copy:", copy_acc.balance, copy_acc.transactions)
print("Copy access:", copy_acc.last_access)

这个 Account 类实现了自定义的复制行为。 balance 被原样复制,transactions 获得浅拷贝,last_access 获得一个特殊值。

对原始 balance 的更改不会影响副本(原始类型),但如果没有在 __copy__ 中显式调用 copy(),事务列表更改将影响副本。

复制不可变对象

对于不可变对象,由于对象无法修改,__copy__ 通常可以只返回 self。此示例演示了此优化。

immutable_copy.py
import copy

class ImmutablePoint:
    def __init__(self, x, y):
        self._x = x
        self._y = y
    
    @property
    def x(self):
        return self._x
    
    @property
    def y(self):
        return self._y
    
    def __copy__(self):
        print("Immutable copy")
        return self  # Safe because object is immutable
    
    def __repr__(self):
        return f"Point({self.x}, {self.y})"

point = ImmutablePoint(3, 4)
point_copy = copy.copy(point)
print(point is point_copy)  # True, same object

这个不可变的 point 类从 __copy__ 返回自身,因为它的状态无法更改。这对于不可变对象是安全有效的。

is 检查确认两个变量都引用同一个对象。 这种优化避免了为不可变类型创建不必要的对象。

将 __copy__ 与 __deepcopy__ 结合使用

这个例子展示了一个类同时实现复制方法,以适当地处理不同的复制场景。

combined_copy.py
import copy

class Node:
    def __init__(self, value, children=None):
        self.value = value
        self.children = children if children is not None else []
    
    def __copy__(self):
        print("Shallow copying Node")
        return Node(self.value, self.children)
    
    def __deepcopy__(self, memo):
        print("Deep copying Node")
        return Node(copy.deepcopy(self.value, memo),
                   [copy.deepcopy(child, memo) for child in self.children])

root = Node(1, [Node(2), Node(3)])
shallow_root = copy.copy(root)
deep_root = copy.deepcopy(root)

root.children.append(Node(4))
print("Original children count:", len(root.children))
print("Shallow copy children count:", len(shallow_root.children))
print("Deep copy children count:", len(deep_root.children))

这个 Node 类实现了两种复制方法。 浅拷贝共享子引用,而深拷贝创建完整的独立树结构。

在向原始对象添加子对象后,浅拷贝反映了此更改(共享引用),而深拷贝保持其原始结构。

最佳实践

资料来源

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。 我自 2007 年以来一直在撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程