【问题标题】:Global dictionaries don't need keyword global to modify them? [duplicate]全局字典不需要关键字 global 来修改它们吗? [复制]
【发布时间】:2012-12-28 17:31:15
【问题描述】:

我想知道为什么我可以在没有global 关键字的情况下更改全局字典?为什么它对其他类型是强制性的?这背后有什么逻辑吗?

例如代码:

#!/usr/bin/env python3

stringvar = "mod"
dictvar = {'key1': 1,
           'key2': 2}

def foo():
    dictvar['key1'] += 1

def bar():
    stringvar = "bar"
    print(stringvar)

print(dictvar)
foo()
print(dictvar)

print(stringvar)
bar()
print(stringvar)

给出以下结果:

me@pc:~/$ ./globalDict.py 
{'key2': 2, 'key1': 1}
{'key2': 2, 'key1': 2}  # Dictionary value has been changed
mod
bar
mod

我期望的地方:

me@pc:~/$ ./globalDict.py 
{'key2': 2, 'key1': 1}
{'key2': 2, 'key1': 1}  # I didn't use global, so dictionary remains the same
mod
bar
mod

【问题讨论】:

  • 我读过SO: Global keyword in PythonSO: Why is keyword global not required,所以我知道没有global 时字典是如何工作的,但我仍然不明白为什么Python 根据变量类型有不同的作用域方法?
  • 你还没有理解 Python 中可变对象和不可变对象的区别。如果您阅读了docs.python.org/2/reference/datamodel.html(至少是第 3.1 小节,不要因为您认为您已经知道它而跳过其中的部分内容),那么它应该会变得清晰。
  • 在这个问题中没有特定于 Python 3 的内容。

标签: python dictionary scope global-variables global


【解决方案1】:

您可以在不使用global 关键字的情况下修改任何可变对象。

这在 Python 中是可能的,因为global 用于将新对象重新分配给已在全局范围内使用的变量名或定义新的全局变量。

但对于可变对象,您无需重新分配任何东西,您只是在原地修改它们,因此 Python 只需从全局范围加载它们并修改它们。

正如docs 所说:

没有全局变量就不可能赋值给全局变量。

In [101]: dic = {}

In [102]: lis = []

In [103]: def func():
    dic['a'] = 'foo'
    lis.append('foo') # but  fails for lis += ['something']
   .....:     

In [104]: func()

In [105]: dic, lis
Out[105]: ({'a': 'foo'}, ['foo'])

dis.dis:

In [121]: dis.dis(func)
  2           0 LOAD_CONST               1 ('foo')
              3 LOAD_GLOBAL              0 (dic)     # the global object dic is loaded
              6 LOAD_CONST               2 ('a')
              9 STORE_SUBSCR                         # modify the same object

  3          10 LOAD_GLOBAL              1 (lis)    # the global object lis is loaded
             13 LOAD_ATTR                2 (append)
             16 LOAD_CONST               1 ('foo')
             19 CALL_FUNCTION            1
             22 POP_TOP             
             23 LOAD_CONST               0 (None)
             26 RETURN_VALUE  

【讨论】:

  • 我知道我能做到(以及背后的机制)。困扰我的是为什么 Python 会为 global 声明例外?
  • @Jovik:没有例外。您对变量赋值和值操作感到困惑。你不能做var = 'string'var.insert(0, 'othertext'),因为字符串是不可变的,你只能替换变量指向的值。但是你可以改变一个可变的。你可以做var1 = {}var2 = var1var2.update({'another': 'dict'}),你会看到var1反映了同样的变化。您更改了一个可变值,而不是指向它的变量。
【解决方案2】:

原因是行

stringvar = "bar"

是不明确的,它可能是指一个全局变量,它可能是创建一个名为stringvar的新局部变量。在这种情况下,Python 默认假定它是一个局部变量,除非已经使用了 global 关键字。

但是,行

dictvar['key1'] += 1

完全没有歧义。它只能引用全局变量dictvar,因为dictvar 必须已经存在才能使语句不抛出错误。

这并不特定于字典 - 列表也是如此:

listvar = ["hello", "world"]

def listfoo():
    listvar[0] = "goodbye"

或其他种类的物体:

class MyClass:
    foo = 1
myclassvar = MyClass()

def myclassfoo():
    myclassvar.foo = 2

mutating operation is used rather than a rebinding one 的任何时候都是如此。

【讨论】:

  • stringvar = "bar" 仅在您考虑使用 Python 以外的语言时才会产生歧义。有人可以从中获得的唯一勉强正面的信息是答案是最底部的链接。
  • @mmgp:我想你误解了我的意思。我并不是说这种行为是模棱两可或未定义的。 OP 在问为什么 Python 的行为如此,我在解释为​​什么它没有选择 - 因为它不知道你什么时候有行 stringvar = "bar" 是指引用全局变量还是创建一个局部变量。因此它默认创建一个局部变量。
  • 那么如果有一个本地字典也称为dictvar,它会在全局字典之前首先被调用吗?
  • @Jin 是的,没错!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-09
  • 1970-01-01
相关资源
最近更新 更多