【问题标题】:Why changing global does not give error?为什么改变全局不会出错?
【发布时间】:2011-05-24 00:05:40
【问题描述】:

为什么 Python 在函数中允许更改不是全局声明的列表?

重新更新

numbers = []
num = 4

def add(n, thisnum=None):
    # changing global list without global declaration!
    numbers.append(n)
    if thisnum:
         num = thisnum
         print 'num assigned', thisnum
    ##numbers = ('one', 'two', 'three')
    ## adding this line makes error:
"""Traceback (most recent call last):
  File "J:\test\glob_vals.py", line 13, in <module>
    add(i)
  File "J:\test\glob_vals.py", line 6, in add
    numbers.append(n)
UnboundLocalError: local variable 'numbers' referenced before assignment
"""

for i in (1,2,3,564,234,23):
    add(i)

print numbers
add(10, thisnum= 19)
# no error
print num
# let the fun begin
num = [4]
add(10, num)
print num

# prints:
"""[1, 2, 3, 56, 234, 23]
num assigned 19
4
num assigned [4]
[4]

"""

如果我将赋值分配给具有相同名称的变量,那么 该行之前 的操作将变为错误,而不是添加的行(我猜是字节码编译器发现了它)。

【问题讨论】:

  • 您似乎对范围规则感到困惑。在这方面有一些问题,质量答案遍布 SO,docs.python.org 也涵盖了这一点。
  • 我仍然认为这不是一件微不足道的事情,或者从我自 1984 年以来作为教师和计算机科学专业学习的文档中显而易见。
  • 我个人认为这很简单 - 可能比大多数语言中的范围规则更复杂,但仍然只有少数规则没有令人讨厌的例外。
  • 感觉至少比 Ada 少很多痛苦,我们从中了解到 STRONG 类型意味着什么(幸运的是,Ada 编译器没有大型机时间来折磨我们,所以这只是理论上的)。
  • 等等,什么?每种语言都有其规则,而 Ada 在其他所有领域肯定有更多规则。当你不知道规则时,你不能期望事情会顺利进行。

标签: python list global side-effects


【解决方案1】:

您没有分配给全局变量,而是调用了一个更改其内容的方法。这是允许的。

如果没有 global 关键字,你不能做的是:

def add(n):
    #global numbers
    numbers = numbers + [n]

结果:

回溯(最近一次通话最后): 文件“C:\Users\Mark\Desktop\stackoverflow\python\test.py”,第 8 行,在 添加(一) 文件“C:\Users\Mark\Desktop\stackoverflow\python\test.py”,第 5 行,添加 数字 = 数字 + [n] UnboundLocalError:分配前引用的局部变量“数字”

不同之处在于,这里我没有改变现有列表 - 我正在尝试创建一个新列表并重新分配回全局范围。但如果没有 global 关键字,这将无法完成。


关于您的更新:

以下行是可以的,因为它在函数范围内创建了一个新的本地名称num。这不会影响全局范围内的变量值。

num = thisnum

【讨论】:

    【解决方案2】:

    global x 影响x = ...(即,它重新分配全局x,而不是创建一个独立的本地x)。它不会影响x.member = ...(因为这是一个方法调用)或x.mutating_method(...),因为 Python(this 不是静态与动态的问题,顺便说一句)不知道(并且不知道) '不在乎)这些方法以某种方式修改self - 所以你必须防止对全局变量(指向的对象)的方法调用......这当然是没有意义的。

    关于更新: 当您执行num = thisnum 时,您所做的事情与numbers.append(n) 完全不同——您正在创建一个局部变量(因为您没有声明global num)并为其分配一些值。这甚至从未触及全球num

    【讨论】:

    • @Tony: 见我的第一句话 - 如果 x 在函数中的某个位置被分配,Python 假定它是本地的,并且正如错误消息告诉你的那样,本地 numbers(它是本地因为它在函数后面分配的,并且没有声明为global)在那个时候没有分配给它的值。
    • 我明白,但它会产生很好的“回到未来”效果,导致之前正常的行由于后面的行而失败。它只是强调字节编译步骤的存在。并且将列表作为变量传递,而不是与直接参数写入相同的值,从而将副作用返回给调用者。
    • @Tony:如果你想强制执行不变性,请使用 Haskell ;) 你会建议什么?
    【解决方案3】:

    因为你只是追加到一个列表。

    访问和分配是不同的概念。当您附加一个列表时,您只是在调用一个更改其值的方法。假设你做了+=,那就是分配。

    如果你这样做:

    >>> numbers = []
    >>> def add(n):
          numbers += n
    
    >>> n = [1, 2]
    >>> add(n)
    

    它会失败,因为这是分配。

    要解决此问题,请在 add() 函数中添加:

    >>> def add(n):
            global numbers
            numbers += n
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-06
      • 1970-01-01
      相关资源
      最近更新 更多