【问题标题】:Difference between local and global variables? [duplicate]局部变量和全局变量的区别? [复制]
【发布时间】:2013-01-06 03:11:51
【问题描述】:

可能重复:
Python passing list as argument

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

我有这个代码:

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 将保持不变,因为它们是全局变量。

【问题讨论】:

  • 这是部分重复,但我认为这并不涵盖 OP 的两种情况。

标签: python variables global local


【解决方案1】:

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

【讨论】:

  • 但是...当我将第 4 行设置为 'L2 += [4]' 时,L2 被修改了,为什么?
  • 因为+= 运算符更改了赋值左侧的对象。它被称为“就地”运算符。 x += y 等价于 x = operator.iadd(x, y)list.iadd(x, y) 在这种情况下返回修改后的列表。
  • 查看Inplace Operators上的文档。
【解决方案2】:

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

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[:]

为了扩展我添加的内联 cmets:

  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 L2Lista3 将与L2相同,因此它们将是同一个列表对象的两个名称。

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

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

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

【讨论】:

    【解决方案3】:

    当您执行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 Language Reference 部分解释了差异:

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

    【讨论】:

    • 但是...当我将第 4 行设置为 'L2 += [4]' 时,L2 被修改了,为什么?
    • @PTF 简而言之,因为+= 很奇怪。它不是L2 = L2 + [4] 的简写,它调用__iadd__(而不是__add__)进行就地修改而不是创建新对象(但仅适用于定义__iadd__ 的列表和其他一些类型) )。
    【解决方案4】:

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

    for elemento in L2:
        L1.append(elemento)
    

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

    如果您在 Google 上搜索“按引用传递”一词,您应该能够在 Python 中找到一些很好的解释性示例。

    【讨论】:

    • “列表通过引用传递”在两个方面具有误导性:(1) 列表并不特殊,所有内容都以相同的方式传递。 (2) 除非您使用“按引用传递”的怪异非标准定义,否则不会通过引用传递任何内容。已提议“通过共享/对象调用”以避免这些问题。
    • 你说得对。我在使用“按引用传递”一词时很粗心。不幸的是,我不知道“按值传递的引用”的规范术语。如果我们将“共享调用”或“对象调用”标准化,那就太好了,但它们似乎不是众所周知的术语.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-17
    • 2022-06-16
    • 2015-10-21
    • 2019-06-05
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    相关资源
    最近更新 更多