Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 818e18d

Browse files
Issue #26167: Minimized overhead in copy.copy() and copy.deepcopy().
Optimized copying and deepcopying bytearrays, NotImplemented, slices, short lists, tuples, dicts, sets.
1 parent de128e1 commit 818e18d

3 files changed

Lines changed: 102 additions & 72 deletions

File tree

Lib/copy.py

Lines changed: 49 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ class instances).
5151
import types
5252
import weakref
5353
from copyreg import dispatch_table
54-
import builtins
5554

5655
class Error(Exception):
5756
pass
@@ -102,37 +101,33 @@ def copy(x):
102101
else:
103102
raise Error("un(shallow)copyable object of type %s" % cls)
104103

105-
return _reconstruct(x, rv, 0)
104+
if isinstance(rv, str):
105+
return x
106+
return _reconstruct(x, None, *rv)
106107

107108

108109
_copy_dispatch = d = {}
109110

110111
def _copy_immutable(x):
111112
return x
112-
for t in (type(None), int, float, bool, str, tuple,
113-
bytes, frozenset, type, range,
114-
types.BuiltinFunctionType, type(Ellipsis),
113+
for t in (type(None), int, float, bool, complex, str, tuple,
114+
bytes, frozenset, type, range, slice,
115+
types.BuiltinFunctionType, type(Ellipsis), type(NotImplemented),
115116
types.FunctionType, weakref.ref):
116117
d[t] = _copy_immutable
117118
t = getattr(types, "CodeType", None)
118119
if t is not None:
119120
d[t] = _copy_immutable
120-
for name in ("complex", "unicode"):
121-
t = getattr(builtins, name, None)
122-
if t is not None:
123-
d[t] = _copy_immutable
124-
125-
def _copy_with_constructor(x):
126-
return type(x)(x)
127-
for t in (list, dict, set):
128-
d[t] = _copy_with_constructor
129-
130-
def _copy_with_copy_method(x):
131-
return x.copy()
121+
122+
d[list] = list.copy
123+
d[dict] = dict.copy
124+
d[set] = set.copy
125+
d[bytearray] = bytearray.copy
126+
132127
if PyStringMap is not None:
133-
d[PyStringMap] = _copy_with_copy_method
128+
d[PyStringMap] = PyStringMap.copy
134129

135-
del d
130+
del d, t
136131

137132
def deepcopy(x, memo=None, _nil=[]):
138133
"""Deep copy operation on arbitrary Python objects.
@@ -179,7 +174,10 @@ def deepcopy(x, memo=None, _nil=[]):
179174
else:
180175
raise Error(
181176
"un(deep)copyable object of type %s" % cls)
182-
y = _reconstruct(x, rv, 1, memo)
177+
if isinstance(rv, str):
178+
y = x
179+
else:
180+
y = _reconstruct(x, memo, *rv)
183181

184182
# If is its own copy, don't memoize.
185183
if y is not x:
@@ -193,13 +191,11 @@ def _deepcopy_atomic(x, memo):
193191
return x
194192
d[type(None)] = _deepcopy_atomic
195193
d[type(Ellipsis)] = _deepcopy_atomic
194+
d[type(NotImplemented)] = _deepcopy_atomic
196195
d[int] = _deepcopy_atomic
197196
d[float] = _deepcopy_atomic
198197
d[bool] = _deepcopy_atomic
199-
try:
200-
d[complex] = _deepcopy_atomic
201-
except NameError:
202-
pass
198+
d[complex] = _deepcopy_atomic
203199
d[bytes] = _deepcopy_atomic
204200
d[str] = _deepcopy_atomic
205201
try:
@@ -211,15 +207,16 @@ def _deepcopy_atomic(x, memo):
211207
d[types.FunctionType] = _deepcopy_atomic
212208
d[weakref.ref] = _deepcopy_atomic
213209

214-
def _deepcopy_list(x, memo):
210+
def _deepcopy_list(x, memo, deepcopy=deepcopy):
215211
y = []
216212
memo[id(x)] = y
213+
append = y.append
217214
for a in x:
218-
y.append(deepcopy(a, memo))
215+
append(deepcopy(a, memo))
219216
return y
220217
d[list] = _deepcopy_list
221218

222-
def _deepcopy_tuple(x, memo):
219+
def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
223220
y = [deepcopy(a, memo) for a in x]
224221
# We're not going to put the tuple in the memo, but it's still important we
225222
# check for it, in case the tuple contains recursive mutable structures.
@@ -236,7 +233,7 @@ def _deepcopy_tuple(x, memo):
236233
return y
237234
d[tuple] = _deepcopy_tuple
238235

239-
def _deepcopy_dict(x, memo):
236+
def _deepcopy_dict(x, memo, deepcopy=deepcopy):
240237
y = {}
241238
memo[id(x)] = y
242239
for key, value in x.items():
@@ -248,7 +245,9 @@ def _deepcopy_dict(x, memo):
248245

249246
def _deepcopy_method(x, memo): # Copy instance methods
250247
return type(x)(x.__func__, deepcopy(x.__self__, memo))
251-
_deepcopy_dispatch[types.MethodType] = _deepcopy_method
248+
d[types.MethodType] = _deepcopy_method
249+
250+
del d
252251

253252
def _keep_alive(x, memo):
254253
"""Keeps a reference to the object x in the memo.
@@ -266,31 +265,15 @@ def _keep_alive(x, memo):
266265
# aha, this is the first one :-)
267266
memo[id(memo)]=[x]
268267

269-
def _reconstruct(x, info, deep, memo=None):
270-
if isinstance(info, str):
271-
return x
272-
assert isinstance(info, tuple)
273-
if memo is None:
274-
memo = {}
275-
n = len(info)
276-
assert n in (2, 3, 4, 5)
277-
callable, args = info[:2]
278-
if n > 2:
279-
state = info[2]
280-
else:
281-
state = None
282-
if n > 3:
283-
listiter = info[3]
284-
else:
285-
listiter = None
286-
if n > 4:
287-
dictiter = info[4]
288-
else:
289-
dictiter = None
268+
def _reconstruct(x, memo, func, args,
269+
state=None, listiter=None, dictiter=None,
270+
deepcopy=deepcopy):
271+
deep = memo is not None
272+
if deep and args:
273+
args = (deepcopy(arg, memo) for arg in args)
274+
y = func(*args)
290275
if deep:
291-
args = deepcopy(args, memo)
292-
y = callable(*args)
293-
memo[id(x)] = y
276+
memo[id(x)] = y
294277

295278
if state is not None:
296279
if deep:
@@ -309,22 +292,22 @@ def _reconstruct(x, info, deep, memo=None):
309292
setattr(y, key, value)
310293

311294
if listiter is not None:
312-
for item in listiter:
313-
if deep:
295+
if deep:
296+
for item in listiter:
314297
item = deepcopy(item, memo)
315-
y.append(item)
298+
y.append(item)
299+
else:
300+
for item in listiter:
301+
y.append(item)
316302
if dictiter is not None:
317-
for key, value in dictiter:
318-
if deep:
303+
if deep:
304+
for key, value in dictiter:
319305
key = deepcopy(key, memo)
320306
value = deepcopy(value, memo)
321-
y[key] = value
307+
y[key] = value
308+
else:
309+
for key, value in dictiter:
310+
y[key] = value
322311
return y
323312

324-
del d
325-
326-
del types
327-
328-
# Helper for instance creation without calling __init__
329-
class _EmptyClass:
330-
pass
313+
del types, weakref, PyStringMap

Lib/test/test_copy.py

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,24 +95,67 @@ def f():
9595
pass
9696
class WithMetaclass(metaclass=abc.ABCMeta):
9797
pass
98-
tests = [None, 42, 2**100, 3.14, True, False, 1j,
98+
tests = [None, ..., NotImplemented,
99+
42, 2**100, 3.14, True, False, 1j,
99100
"hello", "hello\u1234", f.__code__,
100-
b"world", bytes(range(256)),
101-
NewStyle, range(10), Classic, max, WithMetaclass]
101+
b"world", bytes(range(256)), range(10), slice(1, 10, 2),
102+
NewStyle, Classic, max, WithMetaclass]
102103
for x in tests:
103104
self.assertIs(copy.copy(x), x)
104105

105106
def test_copy_list(self):
106107
x = [1, 2, 3]
107-
self.assertEqual(copy.copy(x), x)
108+
y = copy.copy(x)
109+
self.assertEqual(y, x)
110+
self.assertIsNot(y, x)
111+
x = []
112+
y = copy.copy(x)
113+
self.assertEqual(y, x)
114+
self.assertIsNot(y, x)
108115

109116
def test_copy_tuple(self):
110117
x = (1, 2, 3)
111-
self.assertEqual(copy.copy(x), x)
118+
self.assertIs(copy.copy(x), x)
119+
x = ()
120+
self.assertIs(copy.copy(x), x)
121+
x = (1, 2, 3, [])
122+
self.assertIs(copy.copy(x), x)
112123

113124
def test_copy_dict(self):
114125
x = {"foo": 1, "bar": 2}
115-
self.assertEqual(copy.copy(x), x)
126+
y = copy.copy(x)
127+
self.assertEqual(y, x)
128+
self.assertIsNot(y, x)
129+
x = {}
130+
y = copy.copy(x)
131+
self.assertEqual(y, x)
132+
self.assertIsNot(y, x)
133+
134+
def test_copy_set(self):
135+
x = {1, 2, 3}
136+
y = copy.copy(x)
137+
self.assertEqual(y, x)
138+
self.assertIsNot(y, x)
139+
x = set()
140+
y = copy.copy(x)
141+
self.assertEqual(y, x)
142+
self.assertIsNot(y, x)
143+
144+
def test_copy_frozenset(self):
145+
x = frozenset({1, 2, 3})
146+
self.assertIs(copy.copy(x), x)
147+
x = frozenset()
148+
self.assertIs(copy.copy(x), x)
149+
150+
def test_copy_bytearray(self):
151+
x = bytearray(b'abc')
152+
y = copy.copy(x)
153+
self.assertEqual(y, x)
154+
self.assertIsNot(y, x)
155+
x = bytearray()
156+
y = copy.copy(x)
157+
self.assertEqual(y, x)
158+
self.assertIsNot(y, x)
116159

117160
def test_copy_inst_vanilla(self):
118161
class C:

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ Core and Builtins
201201
Library
202202
-------
203203

204+
- Issue #26167: Minimized overhead in copy.copy() and copy.deepcopy().
205+
Optimized copying and deepcopying bytearrays, NotImplemented, slices,
206+
short lists, tuples, dicts, sets.
207+
204208
- Issue #25718: Fixed pickling and copying the accumulate() iterator with
205209
total is None.
206210

0 commit comments

Comments
 (0)