Skip to content

Commit aeab6f7

Browse files
committed
gh-150815: Speed up copy.deepcopy() of containers with atomic elements
The dict, list and tuple deep-copiers send every element back through deepcopy(), paying a function call even for atomic immutable elements that deepcopy() returns unchanged. Inline the atomic-type check into the three copiers so those elements are returned as-is. Behavior is identical, including shared references, recursion and int/tuple subclasses.
1 parent 2f8f569 commit aeab6f7

2 files changed

Lines changed: 17 additions & 6 deletions

File tree

Lib/copy.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,17 +169,20 @@ def deepcopy(x, memo=None):
169169
_deepcopy_dispatch = d = {}
170170

171171

172-
def _deepcopy_list(x, memo, deepcopy=deepcopy):
172+
def _deepcopy_list(x, memo, deepcopy=deepcopy, _atomic=_atomic_types):
173173
y = []
174174
memo[id(x)] = y
175175
append = y.append
176176
for a in x:
177-
append(deepcopy(a, memo))
177+
# Inline the atomic-type check so atomic elements (int, str, None, ...)
178+
# skip the deepcopy() call overhead entirely; deepcopy() would just
179+
# return them unchanged after the same check.
180+
append(a if type(a) in _atomic else deepcopy(a, memo))
178181
return y
179182
d[list] = _deepcopy_list
180183

181-
def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
182-
y = [deepcopy(a, memo) for a in x]
184+
def _deepcopy_tuple(x, memo, deepcopy=deepcopy, _atomic=_atomic_types):
185+
y = [a if type(a) in _atomic else deepcopy(a, memo) for a in x]
183186
# We're not going to put the tuple in the memo, but it's still important we
184187
# check for it, in case the tuple contains recursive mutable structures.
185188
try:
@@ -195,11 +198,15 @@ def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
195198
return y
196199
d[tuple] = _deepcopy_tuple
197200

198-
def _deepcopy_dict(x, memo, deepcopy=deepcopy):
201+
def _deepcopy_dict(x, memo, deepcopy=deepcopy, _atomic=_atomic_types):
199202
y = {}
200203
memo[id(x)] = y
201204
for key, value in x.items():
202-
y[deepcopy(key, memo)] = deepcopy(value, memo)
205+
# Inline the atomic-type check for keys and values: atomic objects
206+
# (str keys, int/str/None values, ...) are returned as-is by
207+
# deepcopy(), so skip the per-item call in that common case.
208+
y[key if type(key) in _atomic else deepcopy(key, memo)] = (
209+
value if type(value) in _atomic else deepcopy(value, memo))
203210
return y
204211
d[dict] = _deepcopy_dict
205212

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Speed up :func:`copy.deepcopy` of :class:`dict`, :class:`list` and
2+
:class:`tuple` containers whose elements are atomic immutable objects, by
3+
skipping the per-element recursive call for those elements. Patch by Bernát
4+
Gábor.

0 commit comments

Comments
 (0)