局部变量和全局变量之间的差异



可能重复:
Python将列表作为参数传递

我找了很多关于这方面的话题,但我不明白到底发生了什么。

我有这个代码:

def altera(L1, L2):
    for elemento in L2:
        L1.append(elemento)
    L2 = L2 + [4]
    L1[-1] = 10
    del L2[0]
    return L2[:]
Lista1 = [1,2,3]
Lista2 = [1,2,3]
Lista3 = altera(Lista1, Lista2)
print Lista1
print Lista2
print Lista3

结果是:

[1, 2, 3, 1, 2, 10]
[1, 2, 3]
[2, 3, 4]

我不明白Lista1是怎么修改的,而Lista2不是。然而,在测试代码之前,我认为Lista1Lista2将保持不变,因为它们是全局变量。

列表在Python中通过引用传递。Lista1被修改是因为您直接调用.append:

for elemento in L2:
    L1.append(elemento)

未修改Lista2,因为您未修改列表。您使用了加法运算符L2 = L2 + [4]不会修改L2。相反,它会创建一个新列表并返回结果。

如果你在谷歌上搜索术语"pass-by-reference",你应该能够在Python中找到一些很好的解释性例子。

执行L1.append(elemento)时,调用的方法实际上会更改由变量L1命名的列表。设置L1L2值的所有其他命令实际上只是为新变量创建新名称。

这个版本不会改变任何东西:

def altera(L1, L2):
    for elemento in L2:
        # create a new list and assign name L1
        L1 = L1 + [elemento]
    # create a new list and assign name L2
    L2 = L2 + [4]
    return L2
Lista1 = [1,2,3]
Lista2 = [1,2,3]
Lista3 = altera(Lista1, Lista2)
print Lista1
print Lista2
print Lista3

而这一个是:

def altera(L1, L2):
    for elemento in L2:
        # Call method on L1 that changes it
        L1.append(elemento)
    # Call method on L2 that changes it
    L2.append(4)
    # Change object pointed to by name L1 -- Lista1
    L1[-1] = 10
    # Change object pointed to by name L2 -- Lista2
    del L2[0]
    return L2[:]
Lista1 = [1,2,3]
Lista2 = [1,2,3]
Lista3 = altera(Lista1, Lista2)
print Lista1
print Lista2
print Lista3

然而,L += [2]有一个棘手的问题,它与L = L + 2并不完全相同。关于增强赋值语句的Python语言参考部分解释了差异:

像x+=1这样的增广赋值表达式可以重写为x=x+1,以实现类似但不完全相同的效果。在增强版本中,x只计算一次。此外,在可能的情况下,实际操作会就地执行,这意味着不是创建新对象并将其分配给目标,而是修改旧对象。">

函数中发生了两件不同的事情:

def altera(L1, L2):
    for elemento in L2:
        L1.append(elemento)
        # [1] this ^ alters L1 in place
    L2 = L2 + [4]
    # [2] this ^ creates a new list
    L1[-1] = 10
    del L2[0]
    return L2[:]

为了扩展我在内联中添加的评论:

  1. altera中,变量L1,无论您传入什么(所以它就是Lista1(。它不是一个副本,一个具有相同内容的新列表-它指的是相同的实际对象
  2. 当你分配L2 = L2 + [4]时,你要做两件事:

    1. 创建值为L2 + [4]的新列表
    2. L2设置为指向这个新列表,而不是您传入的任何列表

    如果我们重新命名变量,它将变得显式,并且的工作方式与完全相同:

    L3 = L2 + [4]
    del L3[0]
    return L3[:]
    

    或者,如果你想修改原始的L2(即实际修改Lista2(,你可以进行

    L2.append(4)
    del L2[0]
    return L2[:]
    

    请注意,最后一行仍然意味着Lista3将是与Lista2不同的列表对象,但它将具有相同的值。如果只使用return L2,则Lista3将与L2完全相同,因此它们将是同一列表对象的两个名称。

如果你想防止这种行为,你可以这样调用altera

Lista3 = altera(Lista1[:], Lista2[:])

现在,在altera中,它将处理自己的参数副本,并且不会影响原始的Lista1Lista2

使用L2 = L2 + [4]语句为名称L2分配新值时,它不再引用Lista2。您的大多数其他语句都会更改它们已经引用的对象的值。

相关内容

  • 没有找到相关文章

最新更新