使用new_list=my_list时,对new_list的任何修改都会每次更改my_list。为什么会出现这种情况,以及如何克隆或复制列表以防止出现这种情况?
当前回答
这是因为,行new_list=my_list为变量my_list分配了一个新的引用,即new_list这类似于下面给出的C代码,
int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;
您应该使用复制模块创建新列表
import copy
new_list = copy.deepcopy(my_list)
其他回答
有人告诉我Python 3.3+添加了list.copy()方法,它应该和切片一样快:
newlist = old_list.copy()
使用对象[:]
>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>>
让我惊讶的是,这还没有被提及,所以为了完整起见。。。
您可以使用“splat operator”:*执行列表解包,这也将复制列表的元素。
old_list = [1, 2, 3]
new_list = [*old_list]
new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]
这种方法的明显缺点是它仅在Python 3.5+中可用。
尽管从时间上看,这似乎比其他常用方法表现得更好。
x = [random.random() for _ in range(1000)]
%timeit a = list(x)
%timeit a = x.copy()
%timeit a = x[:]
%timeit a = [*x]
#: 2.47 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.47 µs ± 54.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.39 µs ± 58.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.22 µs ± 43.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
框架挑战:对于您的应用程序,您实际上需要复制吗?
我经常看到试图以某种迭代方式修改列表副本的代码。为了构造一个简单的示例,假设我们有非工作(因为不应该修改x)代码,如:
x = [8, 6, 7, 5, 3, 0, 9]
y = x
for index, element in enumerate(y):
y[index] = element * 2
# Expected result:
# x = [8, 6, 7, 5, 3, 0, 9] <-- this is where the code is wrong.
# y = [16, 12, 14, 10, 6, 0, 18]
自然,人们会问如何使y成为x的副本,而不是同一列表的名称,这样for循环就会做正确的事情。
但这是错误的做法。从功能上讲,我们真正想做的是在原始列表的基础上创建一个新列表。
我们不需要先做一份拷贝,通常也不应该。
当我们需要对每个元素应用逻辑时
这方面的自然工具是列表理解。这样,我们编写逻辑,告诉我们期望结果中的元素如何与原始元素相关联。它简单、优雅、富有表现力;并且我们避免了在for循环中修改y副本的需要(因为分配给迭代变量不会影响列表-原因与我们首先想要副本的原因相同!)。
对于上面的示例,它看起来像:
x = [8, 6, 7, 5, 3, 0, 9]
y = [element * 2 for element in x]
列表理解非常强大;我们还可以使用它们通过带有if子句的规则过滤掉元素,并且我们可以链接for和if子句(它的工作方式与相应的命令式代码类似,相同的子句的顺序相同;只有最终将在结果列表中结束的值才会移到前面,而不是在“最里面”部分)。如果计划是在修改副本以避免问题的同时迭代原始文件,那么通常有一种更令人愉快的方法来实现这一点,即理解过滤列表。
当我们需要按位置拒绝或插入特定元素时
假设我们有这样的东西
x = [8, 6, 7, 5, 3, 0, 9]
y = x
del y[2:-2] # oops, x was changed inappropriately
我们可以通过将我们不需要的部分放在一起来建立一个列表,而不是先创建一个单独的副本来删除我们不想要的部分。因此:
x = [8, 6, 7, 5, 3, 0, 9]
y = x[:2] + x[-2:]
通过切片处理插入、替换等操作是一项练习。只需说明您希望结果包含哪些子序列。这种情况的一个特殊情况是制作一个反向副本-假设我们需要一个新列表(而不仅仅是反向迭代),我们可以通过切片直接创建它,而不是克隆然后使用.reverse。
这些方法(如列表理解)还有一个优点,即它们将所需的结果创建为表达式,而不是通过程序性地就地修改现有对象(并返回None)。这对于以“流畅”风格编写代码更为方便。
要使用的方法取决于要复制的列表的内容。如果列表中包含嵌套的dict,则deepcopy是唯一有效的方法,否则答案中列出的大多数方法(slice、loop[for]、copy、extend、combine或unpack)都将在类似的时间内工作和执行(loop和deepcopy除外,这两种方法执行得最差)。
剧本
from random import randint
from time import time
import copy
item_count = 100000
def copy_type(l1: list, l2: list):
if l1 == l2:
return 'shallow'
return 'deep'
def run_time(start, end):
run = end - start
return int(run * 1000000)
def list_combine(data):
l1 = [data for i in range(item_count)]
start = time()
l2 = [] + l1
end = time()
if type(data) == dict:
l2[0]['test'].append(1)
elif type(data) == list:
l2.append(1)
return {'method': 'combine', 'copy_type': copy_type(l1, l2),
'time_µs': run_time(start, end)}
def list_extend(data):
l1 = [data for i in range(item_count)]
start = time()
l2 = []
l2.extend(l1)
end = time()
if type(data) == dict:
l2[0]['test'].append(1)
elif type(data) == list:
l2.append(1)
return {'method': 'extend', 'copy_type': copy_type(l1, l2),
'time_µs': run_time(start, end)}
def list_unpack(data):
l1 = [data for i in range(item_count)]
start = time()
l2 = [*l1]
end = time()
if type(data) == dict:
l2[0]['test'].append(1)
elif type(data) == list:
l2.append(1)
return {'method': 'unpack', 'copy_type': copy_type(l1, l2),
'time_µs': run_time(start, end)}
def list_deepcopy(data):
l1 = [data for i in range(item_count)]
start = time()
l2 = copy.deepcopy(l1)
end = time()
if type(data) == dict:
l2[0]['test'].append(1)
elif type(data) == list:
l2.append(1)
return {'method': 'deepcopy', 'copy_type': copy_type(l1, l2),
'time_µs': run_time(start, end)}
def list_copy(data):
l1 = [data for i in range(item_count)]
start = time()
l2 = list.copy(l1)
end = time()
if type(data) == dict:
l2[0]['test'].append(1)
elif type(data) == list:
l2.append(1)
return {'method': 'copy', 'copy_type': copy_type(l1, l2),
'time_µs': run_time(start, end)}
def list_slice(data):
l1 = [data for i in range(item_count)]
start = time()
l2 = l1[:]
end = time()
if type(data) == dict:
l2[0]['test'].append(1)
elif type(data) == list:
l2.append(1)
return {'method': 'slice', 'copy_type': copy_type(l1, l2),
'time_µs': run_time(start, end)}
def list_loop(data):
l1 = [data for i in range(item_count)]
start = time()
l2 = []
for i in range(len(l1)):
l2.append(l1[i])
end = time()
if type(data) == dict:
l2[0]['test'].append(1)
elif type(data) == list:
l2.append(1)
return {'method': 'loop', 'copy_type': copy_type(l1, l2),
'time_µs': run_time(start, end)}
def list_list(data):
l1 = [data for i in range(item_count)]
start = time()
l2 = list(l1)
end = time()
if type(data) == dict:
l2[0]['test'].append(1)
elif type(data) == list:
l2.append(1)
return {'method': 'list()', 'copy_type': copy_type(l1, l2),
'time_µs': run_time(start, end)}
if __name__ == '__main__':
list_type = [{'list[dict]': {'test': [1, 1]}},
{'list[list]': [1, 1]}]
store = []
for data in list_type:
key = list(data.keys())[0]
store.append({key: [list_unpack(data[key]), list_extend(data[key]),
list_combine(data[key]), list_deepcopy(data[key]),
list_copy(data[key]), list_slice(data[key]),
list_loop(data[key])]})
print(store)
后果
[{"list[dict]": [
{"method": "unpack", "copy_type": "shallow", "time_µs": 56149},
{"method": "extend", "copy_type": "shallow", "time_µs": 52991},
{"method": "combine", "copy_type": "shallow", "time_µs": 53726},
{"method": "deepcopy", "copy_type": "deep", "time_µs": 2702616},
{"method": "copy", "copy_type": "shallow", "time_µs": 52204},
{"method": "slice", "copy_type": "shallow", "time_µs": 52223},
{"method": "loop", "copy_type": "shallow", "time_µs": 836928}]},
{"list[list]": [
{"method": "unpack", "copy_type": "deep", "time_µs": 52313},
{"method": "extend", "copy_type": "deep", "time_µs": 52550},
{"method": "combine", "copy_type": "deep", "time_µs": 53203},
{"method": "deepcopy", "copy_type": "deep", "time_µs": 2608560},
{"method": "copy", "copy_type": "deep", "time_µs": 53210},
{"method": "slice", "copy_type": "deep", "time_µs": 52937},
{"method": "loop", "copy_type": "deep", "time_µs": 834774}
]}]
推荐文章
- 把if-elif-else语句放在一行中?
- 我如何结合两个数据框架?
- 如何计数列表中唯一值的出现
- 为什么Pycharm的检查人员抱怨“d ={}”?
- 如何JSON序列化集?
- 在python中,年龄从出生日期开始
- 使用pip安装SciPy
- 在Python中,我应该如何测试变量是否为None, True或False
- 如何在Python中从毫秒创建datetime ?
- 检查字符串是否包含字符串列表中的元素
- 如何解窝(爆炸)在一个熊猫数据帧列,成多行
- 如何使用pip安装opencv ?
- 在pip冻结命令的输出中“pkg-resources==0.0.0”是什么
- 格式y轴为百分比
- 熊猫连接问题:列重叠但没有指定后缀