python - 列表的可变性,因为它与函数的参数相关

标签 python python-3.x list mutability

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

我的问题是,当我使用函数内的索引对列表进行就地更改时,更改确实会如预期的那样得到反射(reflect),因为可变对象是通过引用传递的。但是,当我直接分配列表时,更改不会得到反射(reflect)。

具体来说,我创建了两个列表 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.

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

最佳答案

如果我们稍微更改您的代码,我们可以使用 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

Note, the numbers aren't significant in themselves other than to help distinguish references to different objects. Running this yourself (or if I ran it again), would cause the ids to change.

With a, you're modifying/mutating a but not attempting to re-assign the reference. That's fine.

With b, you're re-assigning the reference. This will work inside the function (as the "Inside, After, b:" print call shows), but this change will not be reflected outside of the function. b will be restored to reference the original object, ['a'].

As to why...

meaning I am unsure why direct assignment changes the memory location.

Inside your function, a and b are just references to objects. Initially, they reference (the objects referenced by) L1 and L2 respectively because by calling f, you're passing references to those objects.

a[0] = [3] first dereferences a (or L1 in this case), then the [0] index, and sets that value.

In fact, if you look at id(a[0]) before and after that call then that would change. a is a list of references. Try it:

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 重新分配或重新绑定(bind)到新对象。旧对象仍然存在(供以后在函数外部使用)。

最后,我经常使用“引用”这个术语,但 Python 并不完全是一种“按引用传递”语言,rather variable names are bound to objects 。在第二种情况下,您将重新绑定(bind) b,从而永远失去与最初引用的对象 L2 的关联。

关于python - 列表的可变性,因为它与函数的参数相关,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50689079/

相关文章:

python - 在遍历那个确切的 List 的同时对 List 中的所有元素求和

python-3.x - 带有pandas和numpy的Pyinstaller,exe在运行时抛出错误

python - 如何遍历列表理解中的子元素?

python - django模板中显示自己的方法

python - Django-Filter 返回请求对象

python - 如何索引图像 (PIL) 类型数组

python-3.x - 使用同一行中的文本替换多行字符串中的文本

python - 在发送 stdin 输入之前等待子进程的提示

python - 如何在遍历列表时从列表中删除元素?

python - 从列表中删除重复项并删除另一个列表中相同索引处的元素