使用 eval(str()) 而不是 deepcopy() 来深度复制列表列表是一种不好的做法吗?
Is it a bad practice to use eval(str()) instead of deepcopy() to deepcopy a list of lists?
我想知道是否有理由使用 deepcopy 而不是这个:
list1 = [[1,2],[3,4],[5,6]]
list2 = eval(str(list1))
如果有另一种很酷的方法,我很想知道。
使用 eval 要慢得多,因为它需要解析字符串并对其进行解释,而 deepcopy 不需要此步骤,并且您还需要事先转换为字符串,而 deepcopy 则不需要。
这是我机器上两者的结果:
>>> timeit.timeit('copy.deepcopy(ls)', setup='import copy\nls = [[1,2], [3,4]]')
3.9644698999999974
>>> timeit.timeit('eval(str(ls))', setup='ls = [[1,2], [3,4]]')
8.982202999999998
除了性能之外还有很多很好的理由不使用eval
。对于一般缺点,请查看 here.
特别是关于你的例子,我想强调一个例子,那里出现了可怕的错误。想象一下有一个 class Foo
,它存储一个任意值 x
:
class Foo:
def __init__(self, x):
self.x = x
def __repr__(self):
return f'{self.__class__.__name__}'
现在成像,你创建这个class的对象列表,例如:
foos = [Foo(i) for i in range(5)]
如果您现在使用 eval
,尝试创建列表的副本,结果将与原始列表不同:
test = eval(str(foos))
您最终会得到一个 class types
:
的列表,而不是 Foo
个对象的列表
foos = [Foo(i) for i in range(5)]
print(foos)
# [Foo, Foo, Foo, Foo, Foo]
print(foos[0].x)
# 0
test = eval(str(foos))
print(test)
# [<class '__main__.Foo'>, <class '__main__.Foo'>, <class '__main__.Foo'>, <class '__main__.Foo'>, <class '__main__.Foo'>]
print(test[0].x)
# AttributeError: type object 'Foo' has no attribute 'x'
万一你刚刚得到(非复杂的)list of lists
通过列表理解进行复制似乎比 eval
更快更安全:
import timeit
import json
import ujson
import copy
def _eval(l):
return eval(str(l))
def _copy(l):
return copy.deepcopy(l)
def _json(l):
return json.loads(json.dumps(l))
def _ujson(l):
return ujson.loads(ujson.dumps(l))
def _comp(l):
return [x[:] for x in l]
shortList = [[1, 2], [3, 4], [5, 6]]
longList = [[x, x + 1] for x in range(0, 50000)]
for lst in (shortList, longList):
for func in ("_eval", "_copy", "_json", "_ujson", "_comp"):
t1 = timeit.Timer(f"{func}({lst})", f"from __main__ import {func}")
print(f"{func} ran:", t1.timeit(number=1000), "milliseconds")
出局(候选名单):
_eval ran: 0.009196660481393337 milliseconds
_copy ran: 0.005948461592197418 milliseconds
_json ran: 0.004726926796138287 milliseconds
_ujson ran: 0.0011531058698892593 milliseconds
_comp ran: 0.00045751314610242844 milliseconds
输出(长列表):
_eval ran: 16.720303252339363 milliseconds
_copy ran: 7.898970659822226 milliseconds
_json ran: 2.1138144126161933 milliseconds
_ujson ran: 1.2348785381764174 milliseconds
_comp ran: 0.5541304731741548 milliseconds
我想知道是否有理由使用 deepcopy 而不是这个:
list1 = [[1,2],[3,4],[5,6]]
list2 = eval(str(list1))
如果有另一种很酷的方法,我很想知道。
使用 eval 要慢得多,因为它需要解析字符串并对其进行解释,而 deepcopy 不需要此步骤,并且您还需要事先转换为字符串,而 deepcopy 则不需要。
这是我机器上两者的结果:
>>> timeit.timeit('copy.deepcopy(ls)', setup='import copy\nls = [[1,2], [3,4]]')
3.9644698999999974
>>> timeit.timeit('eval(str(ls))', setup='ls = [[1,2], [3,4]]')
8.982202999999998
除了性能之外还有很多很好的理由不使用eval
。对于一般缺点,请查看 here.
特别是关于你的例子,我想强调一个例子,那里出现了可怕的错误。想象一下有一个 class Foo
,它存储一个任意值 x
:
class Foo:
def __init__(self, x):
self.x = x
def __repr__(self):
return f'{self.__class__.__name__}'
现在成像,你创建这个class的对象列表,例如:
foos = [Foo(i) for i in range(5)]
如果您现在使用 eval
,尝试创建列表的副本,结果将与原始列表不同:
test = eval(str(foos))
您最终会得到一个 class types
:
Foo
个对象的列表
foos = [Foo(i) for i in range(5)]
print(foos)
# [Foo, Foo, Foo, Foo, Foo]
print(foos[0].x)
# 0
test = eval(str(foos))
print(test)
# [<class '__main__.Foo'>, <class '__main__.Foo'>, <class '__main__.Foo'>, <class '__main__.Foo'>, <class '__main__.Foo'>]
print(test[0].x)
# AttributeError: type object 'Foo' has no attribute 'x'
万一你刚刚得到(非复杂的)list of lists
通过列表理解进行复制似乎比 eval
更快更安全:
import timeit
import json
import ujson
import copy
def _eval(l):
return eval(str(l))
def _copy(l):
return copy.deepcopy(l)
def _json(l):
return json.loads(json.dumps(l))
def _ujson(l):
return ujson.loads(ujson.dumps(l))
def _comp(l):
return [x[:] for x in l]
shortList = [[1, 2], [3, 4], [5, 6]]
longList = [[x, x + 1] for x in range(0, 50000)]
for lst in (shortList, longList):
for func in ("_eval", "_copy", "_json", "_ujson", "_comp"):
t1 = timeit.Timer(f"{func}({lst})", f"from __main__ import {func}")
print(f"{func} ran:", t1.timeit(number=1000), "milliseconds")
出局(候选名单):
_eval ran: 0.009196660481393337 milliseconds
_copy ran: 0.005948461592197418 milliseconds
_json ran: 0.004726926796138287 milliseconds
_ujson ran: 0.0011531058698892593 milliseconds
_comp ran: 0.00045751314610242844 milliseconds
输出(长列表):
_eval ran: 16.720303252339363 milliseconds
_copy ran: 7.898970659822226 milliseconds
_json ran: 2.1138144126161933 milliseconds
_ujson ran: 1.2348785381764174 milliseconds
_comp ran: 0.5541304731741548 milliseconds