我正在用Lutz的书学习Python。我正在使用来自Anaconda发行版的Python 3.6.5。我确实在SO上研究了这个问题,但没有找到任何回答我问题的线程。python中列表的可变性谈论的是append
,而不是我们如何将可变对象传递给函数。
我的问题是,当我使用函数内的索引对列表进行就地更改时,更改确实会像预期的那样反映出来,因为可变对象是通过引用传递的。但是,当我直接分配列表时,更改不会反映出来。
具体来说,我创建了两个列表L1
和L2
。对于L1
,我会使用 index 进行赋值,但对于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'
吗?
如果您认为这篇文章是重复的,如果您标记相关帖子,那就太好了。
顺便说一句,我做了一个小实验,看看是否与基于索引的赋值或直接赋值有关。
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))
输出:
之前,L1: [2] 1870498294152 # L1 之前,L2: ['a'] 1870498294280 # L2 内部,之前,a:1870498294152#L1 内部, 之前, b: 1870498294280 # L2 内部,之后,a:1870498294152#L1 内部,之后,b:1870498239496 # 不同的东西,不是 L2 之后,L1: [[3]] 1870498294152 # L1 之后,L2: ['a'] 1870498294280 # L2
注意,这些数字本身并不重要,只是为了帮助区分对不同对象的引用。 自己运行它(或者如果我再次运行它(会导致 id 发生变化。
使用a
,您可以修改/更改a
但不尝试重新分配引用。 没关系。
使用b
,您将重新分配引用。 这将在函数内部工作(如"Inside, After, b:" 打印调用所示(,但此更改不会反映在函数外部。b
将恢复为引用原始对象,['a']
。
至于为什么...
这意味着我不确定为什么直接分配会更改内存位置。
在函数中,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
重新分配或重新绑定到新对象。 旧对象仍然存在(供以后在函数外部使用(。
最后,我经常使用术语"引用",但Python并不完全是一种"按引用传递"的语言,而是变量名称绑定到对象。 在第二个中,您将重新绑定b
,永远失去与原始引用对象的关联L2
。