python / python/cpython

copy.copy()/copy.deepcopy() of deque and array subclasses drop instance attributes (array also changes type)

未关闭
#152,042 3 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

stdlib type-bug
主要语言
Python
星标
77.2k
派生
35.9k
PR 合并指标
PR 指标待抓取

描述

Bug report

copy.copy() of a collections.deque subclass, and both copy.copy() and copy.deepcopy() of an array.array subclass, silently drop the instance __dict__. For array the result is also a plain array rather than the subclass. pickle preserves both, and list/dict/set subclasses behave correctly, so this is an inconsistency.

Reproducer

import copy, pickle
from collections import deque
from array import array

class D(deque): pass
d = D([1, 2, 3]); d.label = "kept"
print("deque  copy    :", getattr(copy.copy(d),     "label", "LOST"))
print("deque  deepcopy:", getattr(copy.deepcopy(d), "label", "LOST"))
print("deque  pickle  :", getattr(pickle.loads(pickle.dumps(d)), "label", "LOST"))

class A(array): pass
a = A("i", [1, 2, 3]); a.tag = "kept"
print("array  copy    :", type(copy.copy(a)).__name__,     getattr(copy.copy(a),     "tag", "LOST"))
print("array  deepcopy:", type(copy.deepcopy(a)).__name__, getattr(copy.deepcopy(a), "tag", "LOST"))
print("array  pickle  :", type(pickle.loads(pickle.dumps(a))).__name__, getattr(pickle.loads(pickle.dumps(a)), "tag", "LOST"))

Output:

deque  copy    : LOST
deque  deepcopy: kept
deque  pickle  : kept
array  copy    : array LOST
array  deepcopy: array LOST
array  pickle  : A kept

A list subclass (and dict/set) preserves the type and attribute in all three cases, which is the expected behavior:

class L(list): pass
l = L([1, 2, 3]); l.label = "kept"
copy.copy(l).label        # 'kept'
type(copy.copy(l))        # <class '__main__.L'>

Expected

copy.copy()/copy.deepcopy() should return an object of the same (sub)class with the instance attributes preserved, matching pickle and the behavior of list/dict/set subclasses.

Cause

deque.__copy__ (deque_copy in Modules/_collectionsmodule.c) and array.__copy__/__deepcopy__ (Modules/arraymodule.c) build the new object without copying the instance __dict__; the array versions also don't construct the actual subclass. Each type's __reduce_ex__ already captures the subclass and the __dict__ (that's why pickle and deepcopy-of-deque work), so the custom __copy__/__deepcopy__ shortcuts are what diverge.

Versions

Reproduces on main (3.16.0a0) and 3.12.7.

Linked PRs
  • gh-152043

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

阅读 Modules/_collectionsmodule.c 中的 deque_copy 和 Modules/arraymodule.c 中的 array copy methods,然后使用 reproducer 将子类类型和实例属性与 pickle 和 list 子类进行比较。完成的标准是 copy.copy() 和 copy.deepcopy() 对 deque 和 array 保留子类及实例属性,并包含回归测试覆盖。

由索引模型根据 Issue 内容生成。

评估

技术栈
c, python
领域
backend
Issue 类型
缺陷
难度
3/5
预计耗时
1-2 天
活跃度
停滞
描述清晰度
描述清楚
新手友好度
30/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。