copy.copy()/copy.deepcopy() of deque and array subclasses drop instance attributes (array also changes type)
まだ誰も着手していません。
- 主要言語
- 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
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
Modules/_collectionsmodule.c の deque_copy と Modules/arraymodule.c の array のコピー・メソッドを読み、次に reproducer を使って、pickle および list のサブクラスとサブクラスの型およびインスタンス属性を比較します。copy.copy() と copy.deepcopy() が deque と array についてサブクラスとインスタンス属性を保持し、回帰テストのカバレッジがあることが完了条件です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- c, python
- 領域
- backend
- issue の種類
- バグ
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 活発さ
- 停滞
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 30/100