列表的可变性与函数的参数有关

时间:2018-06-04 21:32:31

标签: python python-3.x list mutability

我正在使用Lutz的书学习Python。我正在使用Anaconda发行版的Python 3.6.5。我在SO上研究过这个问题并没有找到任何回答我问题的线程。 Mutability of lists in python讨论append而不是我们如何将可变对象传递给函数。

我的问题是,当我使用函数内部的索引对列表进行就地更改时,更改会反映出来,因为可变对象是通过引用传递的。但是,当我直接指定列表时,更改不会得到反映。

具体来说,我创建了两个列表L1L2。对于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'吗?

如果你认为这篇文章是重复的,那么如果你标记相关帖子就会很棒。

顺便说一句,我进行了一项小型实验,看看是否与基于索引的作业或直接作业有关。

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.

因此,似乎我缺少一个概念,这意味着我不确定为什么直接分配会改变内存位置。

1 个答案:

答案 0 :(得分:3)

如果我们稍微更改您的代码,我们可以使用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,您正在修改/变异a,但不会尝试重新分配参考。没关系。

使用b,您将重新分配参考。这将在内部函数中起作用(如#34; Inside,After,b:&#34;打印调用显示),但此更改不会在外部反映功能。将恢复b以引用原始对象['a']

关于为什么 ...

  

意思是我不确定为什么直接分配会改变内存位置。

在您的函数中,ab只是对象的引用。最初,他们分别引用L1L2引用的对象,因为通过调用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重新分配或重新绑定到新对象。旧对象仍然存在(以后在函数外部使用)。

最后,我使用术语&#34;参考&#34;很多,但Python并不是一个&#34;传递参考&#34;语言,rather variable names are bound to objects。在第二个中,您重新绑定b,永远失去与最初引用的对象L2的关联。