目录
文末分享有几篇我觉得很有帮助的文章,如果你觉得这篇文章对你也有帮助的话
可以点个赞让更多可能有同样困扰的小伙伴看到它呀~
问题来源
想要解决如下问题:按序遍历一串命题词的所有解释。False对应0,True对应1,按序遍历即要求输入2,则输出[[False, False], [False, True], [True, False], [True, True]]。
def genInterpretations(n):
r=['0'*n]*(2**n)
L=[[0]*n]*(2**n) # [[]*n]式创建二维列表,浅拷贝
for i in range(2**n):
b=''
j=i
while i!=0:
b = str(i%2)+b
i = int(i/2)
r[j]='0'*(n-len(b))+b
for k in range(n): # 下标k
if r[j][k]=='0':
L[j][k]=False
else:
L[j][k]=True
print(L)
getInterpretations(2)调用函数,发现输出结果为:[[True, True], [True, True], [True, True], [True, True]],竟然都为True。再试了几组,发现全为True。
这是为什么呢?当时联系猜想到是深浅拷贝的原因,debug一步步查看变量变化,发现果然!给L[j][k]赋值,会给L中每个分量的第k-1个赋值,小实验代码如下。
L=[[0]*2]*(2**2)
print(L) # [[0, 0], [0, 0], [0, 0], [0, 0]]
L[0][0]=1
print(L) # [[1, 0], [1, 0], [1, 0], [1, 0]]
浅拷贝的解释
官方文档
官方文档有解释如下:
Note also that the copies are shallow; nested structures are not copied. This often haunts new Python programmers; consider:
>>> lists = [[]] * 3 >>> lists [[], [], []] >>> lists[0].append(3) >>> lists [[3], [3], [3]]
What has happened is that [[]] is a one-element list containing an empty list, so all three elements of [[]] * 3 are (pointers to) this single empty list.
通俗解释
lists = [[]] * 3 这样的一行只是把[]这个列表的指针复制了三遍存进了lists列表,三个指针指向相同的内存地址,通过其中一个指针改变内存中所存数据,三个指针访问到的数据都被改变。这样只是复制指针,不复制内存地址的操作叫做浅拷贝(Shallow Copy)。
python中的对象有三个要素:id(id() 函数:返回对象的唯一标识,可以类比成该对象在内存中的地址), name, value,我们做个小实验,输出id确认解释。
L=[[0]*2]*(2**2)
print(L) # [[0, 0], [0, 0], [0, 0], [0, 0]]
L[0][0]=1
print(L) # [[1, 0], [1, 0], [1, 0], [1, 0]]
print(id(L[0]), id(L[1])) # 2283586771392 2283586771392 L两个分量的指针相同
形象化的图像解释如下:
解决问题
由此可见,[[]*n]式创建的二维列表是浅拷贝,不能达到我们想要其实现的功能。需要先分别创建子列表,这时的子列表是有不同的内存地址的,再把子列表放入父列表。可以用列表生成式(List Comprehensions)法创建这样的二维列表。
def genInterpretations(n):
r=['0'*n]*(2**n)
L=[[0]*n for i in range((2**n))] # 列表生成式创建二维空数组
for i in range(2**n):
b=''
j=i
while i!=0:
b = str(i%2)+b
i = int(i/2)
r[j]='0'*(n-len(b))+b
for k in range(n): # 下标k
if r[j][k]=='0':
L[j][k]=False
else:
L[j][k]=True
print(L)
成功解决问题,输出想要的结果。
拓展讨论
注意直接用“=”赋值是一种浅拷贝方式,同时可以思考一下如何深拷贝一个二维列表(有父对象,也有子对象,那么分几个层次的“深拷贝”?该如何实现?现成的库函数是什么?)可参考文章:Python 直接赋值、浅拷贝和深度拷贝解析 | 菜鸟教程 (runoob.com)
列表生成式方法:列表生成式 - 廖雪峰的官方网站 (liaoxuefeng.com)
关于我这个问题来源还有更简单的递归做法……再次感叹列表生成式的强大……
递归可参考文章Divide and Conquer Algorithms with Python Examples | Skerritt.blog。这个博主的其他文章也很不错👍
def genInterpretations(n):
if n==0:
return [[]] # 递归终止条件
else:
return [sub+add for sub in genInterpretations(n-1) for add in [[False], [True]]]
# 递归关系式
# 我原来做法是先放入排好序的二进制数,再对二进制数字符遍历输出对应的bool值……
# 其实可以直接绕过二进制数,对bool值进行处理