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

为何copy.deepcopy会导致set.difference结果不符合预期?

为什么copy.deepcopy会影响set.difference的执行行为?

近期发现copy.deepcopy会改变set.difference的执行行为:定义了一个Foo类,实现__hash__方法返回id属性值;创建两个id不同的Foo实例存入数组后,对数组执行深拷贝。计算原数组与深拷贝数组的集合差集时,结果非空;但原数组自身的集合差集为空。

复现代码

import copy


class Foo:
    def __init__(self, id: int) -> None:
        self.id = id

    def __hash__(self) -> int:
        return self.id

    def __repr__(self) -> str:
        return f"<Foo id={self.id}>"


arr = [Foo(1), Foo(2)]
arr_copy = copy.deepcopy(arr)

print(f"hash(arr) -> {', '.join(f'{hash(x)}' for x in arr)}")
print(f"hash(arr_copy) -> {', '.join(f'{hash(x)}' for x in arr_copy)}")

arr_diff = set(arr) - set(arr)  # Empty set
arr_copy_diff = set(arr) - set(arr_copy)  # Set with both Foo's???

print(f"{arr_diff=}")
print(f"{arr_copy_diff=}")

assert arr_diff == arr_copy_diff

问题原因

这并非copy.deepcopy的问题,而是Python集合的元素相等判断规则导致的:

  • 集合判断元素是否相同时,会先通过__hash__比较哈希值;
  • 哈希值相同时,再调用__eq__方法确认是否真正相等。

你的Foo类只实现了__hash__,未实现__eq__,此时Python会使用默认的__eq__逻辑——比较对象的内存地址(身份标识)。深拷贝生成的Foo实例虽然id属性和原实例一致、哈希值相同,但它们是不同的对象,内存地址不同,所以默认__eq__会判定二者不相等。

因此:

  • set(arr) - set(arr)中元素都是同一个对象,差集为空;
  • set(arr) - set(arr_copy)中,原数组元素和拷贝数组元素被认为是不同元素,差集保留原数组的所有元素。

解决方案

在Foo类中同时实现__eq__方法,根据id属性判断实例是否相等:

class Foo:
    def __init__(self, id: int) -> None:
        self.id = id

    def __hash__(self) -> int:
        return self.id

    def __eq__(self, other) -> bool:
        if isinstance(other, Foo):
            return self.id == other.id
        return False

    def __repr__(self) -> str:
        return f"<Foo id={self.id}>"

这样,只要两个Foo实例的id属性相同,__eq__就会返回True,集合会将它们视为相等元素,差集结果就会符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 22:45:37