列表的可变性,因为它与函数的参数有关
Mutability of lists as it relates to the argument of a function
我正在使用 Lutz 的书学习 Python。我正在使用来自 Anaconda 发行版的 Python 3.6.5。我确实在 SO 上研究了这个问题,但没有找到任何可以回答我问题的线程。 谈论 append
而不是我们如何将可变对象传递给函数。
我的问题是,当我在函数内使用索引对列表进行就地更改时,更改确实如预期的那样得到反映,因为可变对象是通过引用传递的。但是,当我直接分配一个列表时,更改不会得到反映。
具体来说,我创建了两个列表 L1
和 L2
。对于L1
,我会使用索引进行赋值,但是对于L2
,我会在函数内部进行直接赋值。
L1=[2]
L2=['a']
print("Before, L1:",L1)
print("Before, L2:",L2)
def f(a,b):
a[0] =[3] #Using index-based assignment
b = ['b'] #Direct assignment
#Pass L to f
f(L1,L2)
print("After, L1:",L1)
print("After, L2:",L2)
输出为:
Before, L1: [2]
Before, L2: ['a']
After, L1: [[3]]
After, L2: ['a']
如我们所见,L1
已更改,但 L2
未更改。
问题:有人可以解释一下为什么 L2
的值没有更改为 'b'
吗?
如果您认为此 post 是重复的,如果您标记相关的 post 就更好了。
顺便说一句,我 运行 做了一个小实验,看看是否与基于索引的赋值或直接赋值有关。
l=[2]
id(l)
l[0] = 3 #Index assignment
id(l) # Memory location doesn't change
l = 3 # direct assignment
id(l) #Memory location changes.
因此,我似乎缺少一个概念,这意味着我不确定为什么直接赋值会更改内存位置。
如果我们稍微更改您的代码,我们可以使用 id
查看引用如何更改(或不更改):
L1=[2]
L2=['a']
print("Before, L1:", L1, id(L1))
print("Before, L2:", L2, id(L2))
def f(a,b):
print("Inside, Before, a:", id(a))
print("Inside, Before, b:", id(b))
a[0] =[3] #Using index-based assignment
b = ['b'] #Direct assignment
print("Inside, After, a:", id(a))
print("Inside, After, b:", id(b))
#Pass L to f
f(L1,L2)
print("After, L1:", L1, id(L1))
print("After, L2:", L2, id(L2))
输出:
Before, L1: [2] 1870498294152 # L1
Before, L2: ['a'] 1870498294280 # L2
Inside, Before, a: 1870498294152 # L1
Inside, Before, b: 1870498294280 # L2
Inside, After, a: 1870498294152 # L1
Inside, After, b: 1870498239496 # Something different, not L2
After, L1: [[3]] 1870498294152 # L1
After, L2: ['a'] 1870498294280 # L2
请注意,数字本身并不重要,只是为了帮助区分对不同对象的引用。 运行 这个你自己(或者如果我再 运行 它),会导致 id 改变。
使用 a
,您 modifying/mutating a
但没有尝试重新分配引用。没关系。
对于 b
,您正在重新分配引用。这将在 内部 函数中起作用(如 "Inside, After, b:" 打印调用所示),但此更改 不会 反映在函数外部. b
将恢复为引用原始对象,['a']
。
至于为什么...
meaning I am unsure why direct assignment changes the memory location.
在您的函数中,a
和 b
只是对对象的引用。最初,它们分别引用 L1
和 L2
(所引用的对象),因为通过调用 f
,您正在传递对这些对象的引用。
a[0] = [3]
首先取消引用 a
(或本例中的 L1
),然后是 [0]
索引,并设置该值。
事实上,如果您查看调用前后的 id(a[0])
,那么 会发生变化。 a
是参考列表。试一试:
print(id(a[0])) # One thing
a[0] =[3] #Using index-based assignment
print(id(a[0])) # Something different
这很好。当您退出函数时,L1
仍将引用函数使用 a
引用的对象,并且它在 0 索引处的突变将保留。
使用 b = ['b']
,您将 b
重新分配或重新绑定到新对象。旧对象仍然存在(供以后在函数外使用)。
最后,我经常使用术语 "reference",但是 Python 并不完全是一种 "pass-by-reference" 语言,rather variable names are bound to objects。在第二个中,您重新绑定 b
,永远失去与最初引用的对象 L2
的关联。
我正在使用 Lutz 的书学习 Python。我正在使用来自 Anaconda 发行版的 Python 3.6.5。我确实在 SO 上研究了这个问题,但没有找到任何可以回答我问题的线程。 append
而不是我们如何将可变对象传递给函数。
我的问题是,当我在函数内使用索引对列表进行就地更改时,更改确实如预期的那样得到反映,因为可变对象是通过引用传递的。但是,当我直接分配一个列表时,更改不会得到反映。
具体来说,我创建了两个列表 L1
和 L2
。对于L1
,我会使用索引进行赋值,但是对于L2
,我会在函数内部进行直接赋值。
L1=[2]
L2=['a']
print("Before, L1:",L1)
print("Before, L2:",L2)
def f(a,b):
a[0] =[3] #Using index-based assignment
b = ['b'] #Direct assignment
#Pass L to f
f(L1,L2)
print("After, L1:",L1)
print("After, L2:",L2)
输出为:
Before, L1: [2]
Before, L2: ['a']
After, L1: [[3]]
After, L2: ['a']
如我们所见,L1
已更改,但 L2
未更改。
问题:有人可以解释一下为什么 L2
的值没有更改为 'b'
吗?
如果您认为此 post 是重复的,如果您标记相关的 post 就更好了。
顺便说一句,我 运行 做了一个小实验,看看是否与基于索引的赋值或直接赋值有关。
l=[2]
id(l)
l[0] = 3 #Index assignment
id(l) # Memory location doesn't change
l = 3 # direct assignment
id(l) #Memory location changes.
因此,我似乎缺少一个概念,这意味着我不确定为什么直接赋值会更改内存位置。
如果我们稍微更改您的代码,我们可以使用 id
查看引用如何更改(或不更改):
L1=[2]
L2=['a']
print("Before, L1:", L1, id(L1))
print("Before, L2:", L2, id(L2))
def f(a,b):
print("Inside, Before, a:", id(a))
print("Inside, Before, b:", id(b))
a[0] =[3] #Using index-based assignment
b = ['b'] #Direct assignment
print("Inside, After, a:", id(a))
print("Inside, After, b:", id(b))
#Pass L to f
f(L1,L2)
print("After, L1:", L1, id(L1))
print("After, L2:", L2, id(L2))
输出:
Before, L1: [2] 1870498294152 # L1 Before, L2: ['a'] 1870498294280 # L2 Inside, Before, a: 1870498294152 # L1 Inside, Before, b: 1870498294280 # L2 Inside, After, a: 1870498294152 # L1 Inside, After, b: 1870498239496 # Something different, not L2 After, L1: [[3]] 1870498294152 # L1 After, L2: ['a'] 1870498294280 # L2
请注意,数字本身并不重要,只是为了帮助区分对不同对象的引用。 运行 这个你自己(或者如果我再 运行 它),会导致 id 改变。
使用 a
,您 modifying/mutating a
但没有尝试重新分配引用。没关系。
对于 b
,您正在重新分配引用。这将在 内部 函数中起作用(如 "Inside, After, b:" 打印调用所示),但此更改 不会 反映在函数外部. b
将恢复为引用原始对象,['a']
。
至于为什么...
meaning I am unsure why direct assignment changes the memory location.
在您的函数中,a
和 b
只是对对象的引用。最初,它们分别引用 L1
和 L2
(所引用的对象),因为通过调用 f
,您正在传递对这些对象的引用。
a[0] = [3]
首先取消引用 a
(或本例中的 L1
),然后是 [0]
索引,并设置该值。
事实上,如果您查看调用前后的 id(a[0])
,那么 会发生变化。 a
是参考列表。试一试:
print(id(a[0])) # One thing
a[0] =[3] #Using index-based assignment
print(id(a[0])) # Something different
这很好。当您退出函数时,L1
仍将引用函数使用 a
引用的对象,并且它在 0 索引处的突变将保留。
使用 b = ['b']
,您将 b
重新分配或重新绑定到新对象。旧对象仍然存在(供以后在函数外使用)。
最后,我经常使用术语 "reference",但是 Python 并不完全是一种 "pass-by-reference" 语言,rather variable names are bound to objects。在第二个中,您重新绑定 b
,永远失去与最初引用的对象 L2
的关联。