【问题标题】:python list mutation in loops, global modifying vs. local modifying循环中的python列表突变,全局修改与局部修改
【发布时间】:2015-10-02 08:22:45
【问题描述】:

Halp,也许比我更熟悉 Python 的人可以回答这个(菜鸟)问题。

我无法弄清楚为什么列表会出现以下行为。

让我们有以下功能:

def foo1(L):
   """
    L is a list
   """
   L = ['new', 'stuff']
   print L

def foo2(L):
   """
    L is a list
   """
   L[:] = ['new', 'stuff']
   print L

IDLE 输出以下内容:

>>>L = [1,2,3]  
>>>foo1(L)  
['new', 'stuff']  
>>>L  
[1,2,3]  

>>>foo2(L)  
['new', 'stuff']  
>>>L  
['new', 'stuff']

我不明白为什么第一个函数只是临时修改列表,而第二个函数会全局修改列表。我认为这不是范围问题,而是 L= 与 L[:]=

的问题

我知道,如果我使用列表推导来更改 L 或使用 L.append() 之类的方法或其他方法在函数中对 L 进行变异,那么它也会全局修改 L。

我对 Python 并不陌生,但通常,我只是在谷歌上搜索解决我的问题的方法,如果它有效,我不会质疑它。今年夏天我要花时间学习最佳实践并深入了解python。

修改: 所以 Martijn 决定将此问题标记为重复,但我不同意。链接的问题没有解决我的问题。

将变量传递给函数时,函数会创建一个具有自己作用域的新环境。此环境中的变量仅存在于该环境的范围内,不存在于其他任何地方。因此,当我的列表 L 被传递给 foo1 和 foo2 时,临时列表 L 仅在该函数调用的范围内生成。换句话说,在调用 foo1(L) 之前,存在一个名为 L 的列表,它等于 [1,2,3]。在调用 foo1(L) 之后,创建了一个新的函数环境,它包含一个临时列表,也称为 L,它只存在于该函数的范围内。如果我在函数内通过 L = [blah, blah, blah] 修改 L,则原始列表 L 不会发生任何变化。但是,通过在函数内的 L 上使用 [:],现在修改了原始列表。为什么? python 如何在创建了一个范围有限的临时变量后知道它需要修改原来的全局范围的变量?这个概念对我来说没有意义。

【问题讨论】:

  • 您正在改变列表对象的内容,而不是创建一个新的列表对象。
  • 最佳实践? Don't mutate global state.
  • 此外,告诉 Python 编辑列表本身的全局版本的第二个函数中的 [:] 运算符是什么?从我在学校学到的知识来看,这两个函数都将“L”视为传递给函数并仅存在于其范围内的一些临时对象。如果是这样的话,那么我很困惑 L[:] 如何以某种方式告诉 Python 它创建的临时变量 L 实际上是我在范围之外创建的列表?如果这有意义的话。
  • 在 foo2 的范围内,L 只是您作为参数传递的列表,您可以对其进行变异。如果您在全局范围内给它一个不同的名称,结果将是相同的,它将被变异。将L[:] = whatever 视为L.clear(); L.extend(whatever) 的等价物,那么它可能会变得更清晰。 Python 不会创建“临时列表”。
  • 您对某些语言如何处理函数范围的看法是正确的,但您所说的对 Python 而言(总是)不正确。这就是为什么mutable default arguments 如此常见的“陷阱”。当你将一个可能很大的对象传递给一个函数(例如列表或字典)时,Python 不会制作它的临时副本(按值传递),它或多或少地传递一个指针。这过于简单,但归结为隐含的引用传递,这就是发生这种情况的原因。

标签: list python-2.7 loops scope


【解决方案1】:

参数 L 和全局变量 L 都引用内存中的相同对象。当您传递对象时,它只是对全局对象的引用,因为这就是引用和赋值在 python 中的工作方式。 因此,当您在 foo1 中执行 ['new', 'stuff'] 时,您将值分配给 L 本地副本,即参数,因为这是第一次查找分配。 但是在foo2 中执行L[:] = ['new', 'stuff'] 时,您正在执行影响全局变量的切片。

您可以通过检查全局和参数 L 的 id 是否相同来进行测试。

L = [1, 2, 3]

B = L

id(B) == id(L)    # True

def foo(L):
   print(id(L) == id(B))

foo(L)    # TRUE

# or..

def foobar(L):
    print(id(locals()['L']) == id(globals()['L']))

希望这会有所帮助。 :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-05
    • 2015-10-04
    • 1970-01-01
    • 2014-06-19
    • 1970-01-01
    • 2017-10-01
    • 1970-01-01
    相关资源
    最近更新 更多