今天我在工作中遇到了一种奇怪的语法方法,我无法理解。假设我有以下列表:
my_list = [[1, 2, 3], [4, 5, 6]]
Run Code Online (Sandbox Code Playgroud)
我的目标是根据某些条件过滤每个嵌套列表并覆盖列表中的元素。因此,假设我想从每个包含my_list偶数列表的嵌套列表中删除奇数,最终结果如下所示:
[[2], [4, 6]]
Run Code Online (Sandbox Code Playgroud)
如果我尝试使用简单的赋值运算符来执行此操作,则它不起作用。
my_list = [[1, 2, 3], [4, 5, 6]]
for l in my_list:
l = [num for num in l if num % 2 == 0]
print(my_list)
Output: [[1, 2, 3], [4, 5, 6]]
Run Code Online (Sandbox Code Playgroud)
但是,如果我“切片”列表,它会提供预期的输出。
my_list = [[1, 2, 3], [4, 5, 6]]
for l in my_list:
l[:] = [num for num in l if num % 2 == 0]
print(my_list)
Output: [[2], [4, 6]]
Run Code Online (Sandbox Code Playgroud)
我最初的假设是,这l是一个新创建的对象,它实际上并不指向列表中的相应对象,但是比较 、 和 (其中 是 in 的索引)的输出id(x[i]),id(l)我id(l[:])意识到这i是具有不同的对象ID。那么,如果当我分配给时Python正在创建一个新对象,那么Python如何知道覆盖现有对象呢?为什么这有效?为什么简单的赋值运算符不起作用?lxl[:]l[:]ll = ...
这很微妙。
片段一:
my_list = [[1, 2, 3], [4, 5, 6]]
for l in my_list:
l = [num for num in l if num % 2 == 0]
Run Code Online (Sandbox Code Playgroud)
为什么这不起作用?因为当您这样做时l = ,您只是重新分配变量l,而不是对其值进行任何更改。
如果我们“手动”编写循环,则希望该策略失败的原因会变得更加清楚:
my_list = [[1, 2, 3], [4, 5, 6]]
# iteration 1
l = my_list[0]
l = [num for num in l if num % 2 == 0]
# iteration 2
l = my_list[1]
l = [num for num in l if num % 2 == 0]
Run Code Online (Sandbox Code Playgroud)
片段二:
my_list = [[1, 2, 3], [4, 5, 6]]
for l in my_list:
l[:] = [num for num in l if num % 2 == 0]
Run Code Online (Sandbox Code Playgroud)
为什么这有效?因为通过使用l[:] = ,您实际上是在修改引用的值l,而不仅仅是变量l。让我详细说明一下。
一般来说,[:]在列表上使用符号(切片符号)允许人们处理列表的一部分。
最简单的用途是从列表中获取值;我们可以编写a[n:k]来获取第nth、item n+1st 项等,直到k-1。例如:
>>> a = ["a", "very", "fancy", "list"]
>>> print(a[1:3])
['very', 'fancy']
Run Code Online (Sandbox Code Playgroud)
Python 还允许在 的左侧使用切片表示法=。在这种情况下,它将符号解释为我们只想更新列表的一部分。例如,我们可以"very", "fancy"像"not", "so", "fancy"这样替换:
>>> print(a)
['a', 'very', 'fancy', 'list']
>>> a[1:3] = ["not", "so", "fancy"]
>>> print(a)
['a', 'not', 'so', 'fancy', 'list']
Run Code Online (Sandbox Code Playgroud)
使用切片语法时,Python 还提供了一些方便的简写。[n:k]我们可以省略nor或两者都省略,而不是写k。
如果我们省略n,那么我们的切片看起来像[:k],Python 将其理解为“直到k”,即与 相同[0:k]。
如果我们省略k,那么我们的切片看起来像a[n:],Python 将其理解为“n以及之后”,即与 相同a[n:len(a)]。
如果我们省略两者,则两个规则都会发生,因此a[:]与 相同a[0:len(a)],它是整个列表的切片。
例子:
>>> print(a)
['a', 'not', 'so', 'fancy', 'list']
>>> print(a[2:4])
['so', 'fancy']
>>> print(a[:4])
['a', 'not', 'so', 'fancy']
>>> print(a[2:])
['so', 'fancy', 'list']
>>> print(a[:])
['a', 'not', 'so', 'fancy', 'list']
Run Code Online (Sandbox Code Playgroud)
至关重要的是,如果我们在 a 的左侧使用切片,这一切仍然适用=:
>>> print(a)
['a', 'not', 'so', 'fancy', 'list']
>>> a[:4] = ["the", "fanciest"]
>>> print(a)
['the', 'fanciest', 'list']
Run Code Online (Sandbox Code Playgroud)
并使用[:]方法替换列表中的每个项目:
>>> print(a)
['the', 'fanciest', 'list']
>>> a[:] = ["something", "completely", "different"]
>>> print(a)
['something', 'completely', 'different']
Run Code Online (Sandbox Code Playgroud)
好的,到目前为止一切顺利。
需要注意的关键一点是,在列表左侧使用切片符号会就地更新列表。换句话说,当我这样做时a[1:3] =,变量a永远不会更新;它引用的列表是。
我们可以用 看到这一点id(),就像您所做的那样:
>>> print(a)
['something', 'completely', 'different']
>>> print(id(a))
139848671387072
>>> a[1:] = ["truly", "amazing"]
>>> print(a)
['something', 'truly', 'amazing']
>>> print(id(a))
139848671387072
Run Code Online (Sandbox Code Playgroud)
也许更相关的是,这意味着如果a是对某个其他对象中的列表的引用,则使用将更新该对象中a[:] =的列表。就像这样:
>>> list_of_lists = [ [1, 2], [3, 4], [5, 6] ]
>>> second_list = list_of_lists[1]
>>> print(second_list)
[3, 4]
>>> second_list[1:] = [2, 1, 'boom!']
>>> print(second_list)
[3, 2, 1, 'boom!']
>>> print(list_of_lists)
[[1, 2], [3, 2, 1, 'boom!'], [5, 6]]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
127 次 |
| 最近记录: |