cod*_*edd 1 python list python-3.x
编辑:这个问题是关于为什么这种行为是这样的,而不是如何绕过它,这就是所谓的重复的内容。
我使用以下符号在不同情况下创建特定大小的列表。例如:
>>> [None] * 5
[None, None, None, None, None]
>>>
Run Code Online (Sandbox Code Playgroud)
这似乎按预期工作并且短于:
>>> [None for _ in range(5)]
[None, None, None, None, None]
>>>
Run Code Online (Sandbox Code Playgroud)
然后我尝试使用相同的方法创建列表列表:
>>> [[]] * 5
[[], [], [], [], []]
>>>
Run Code Online (Sandbox Code Playgroud)
很公平。它似乎按预期工作。
然而,在检查调试器时,我注意到所有子列表存储桶都具有相同的值,即使我只添加了一个项目。例如:
>>> t = [[]] * 5
>>> t
[[], [], [], [], []]
>>> t[1].append(4)
>>> t
[[4], [4], [4], [4], [4]]
>>> t[0] is t[1]
True
>>>
Run Code Online (Sandbox Code Playgroud)
我并不期望所有顶级数组元素都是对单个子列表的引用;我期望有 5 个独立的子列表。
为此,我必须编写如下代码:
>>> t = [[] for _ in range(5)]
>>> t
[[], [], [], [], []]
>>> t[2].append(4)
>>> t
[[], [], [4], [], []]
>>> t[0] is t[1]
False
>>>
Run Code Online (Sandbox Code Playgroud)
我显然遗漏了一些东西,可能是历史事实,或者只是看待这里一致性的不同方式。
有人可以解释为什么两个不同的代码片段,人们合理地期望彼此等效,实际上最终隐式地产生不同的和不明显的(IMO)结果,特别是考虑到Python总是显式和明显的禅宗?
请注意,我已经知道这个问题,这与我要问的不同。
我只是在寻找详细的解释/理由。如果此行为存在历史、技术和/或理论原因,请务必包含一两个参考资料。
当您执行以下操作时:
[[]]*n
Run Code Online (Sandbox Code Playgroud)
您首先创建一个列表,然后使用*带有 的运算符int n。这会获取列表中的任何对象,并创建它的 n 多次重复。
但由于在 Python 中,显式优于隐式,因此您不会隐式地复制这些对象。确实,这符合Python的语义。
尝试举出 Python隐式创建副本的单个案例。
此外,它与列表中添加的内容一致:
l = [1, [], 'a']
l2 = l + l + l
l[1].append('foo')
print(l2)
Run Code Online (Sandbox Code Playgroud)
和输出:
[1, ['foo'], 'a', 1, ['foo'], 'a', 1, ['foo'], 'a']
Run Code Online (Sandbox Code Playgroud)
现在,正如评论中所指出的,对于来自 C++ 的人来说,上面的内容令人惊讶是有道理的,但如果一个人习惯了 Python,那么上面的内容就是人们所期望的。
另一方面:
[[] for _ in range(5)]
Run Code Online (Sandbox Code Playgroud)
是一个列表理解。它相当于:
lst = []
for _ in range(5):
lst.append([])
Run Code Online (Sandbox Code Playgroud)
很明显,每次进入循环时都会创建一个新列表。这就是字面语法的工作原理。
顺便说一句,我几乎从不*在列表上使用运算符,除了我喜欢的一个特定习惯用法:
>>> x = list(range(1, 22))
>>> it_by_three = [iter(x)]*3
>>> for a,b,c in zip(*it_by_three):
... print(a, b, c)
...
1 2 3
4 5 6
7 8 9
10 11 12
13 14 15
16 17 18
19 20 21
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
358 次 |
| 最近记录: |