全局字典不需要使用关键字global来修改它们吗?
全局字典不需要使用关键字global来修改它们吗?
我想知道为什么我可以在不使用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} # 字典的值已经被改变 mod bar mod
而我期望的是:
me@pc:~/$ ./globalDict.py {'key2': 2, 'key1': 1} {'key2': 2, 'key1': 1} # 我没有使用global,所以字典保持不变 mod bar mod
在Python中,可以修改任何可变对象而不使用global
关键字。这是因为global
关键字用于在全局范围内重新分配新对象给已经使用的变量名或定义新的全局变量。
但是,在可变对象的情况下,你并没有重新分配任何内容,只是在原地修改它们,因此Python只是从全局范围加载它们并进行修改。
正如文档所说:
如果没有
global
,将无法对全局变量进行赋值。
下面的代码示例说明了这一点:
In [101]: dic = {} In [102]: lis = [] In [103]: def func(): dic['a'] = 'foo' lis.append('foo') # 但对于lis += ['something']将失败 .....: In [104]: func() In [105]: dic, lis Out[105]: ({'a': 'foo'}, ['foo'])
这里的func
函数在全局字典dic
中添加了一个键值对,并在全局列表lis
中添加了一个元素。这证明了我们可以在不使用global
关键字的情况下修改全局可变对象。
通过使用dis.dis
函数,我们可以查看func
函数的字节码:
In [121]: dis.dis(func) 2 0 LOAD_CONST 1 ('foo') 3 LOAD_GLOBAL 0 (dic) # 加载全局对象dic 6 LOAD_CONST 2 ('a') 9 STORE_SUBSCR # 修改同一个对象 3 10 LOAD_GLOBAL 1 (lis) # 加载全局对象lis 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加载了全局的dic
和lis
对象,并在函数中对它们进行了修改。
关于为什么Python对global
语句进行了特殊处理的问题,答案是并没有进行特殊处理。你可能对变量赋值和值操作之间的区别感到困惑。对于不可变对象(如字符串),你不能进行var = 'string'
或var.insert(0, 'othertext')
这样的操作,因为字符串是不可变的,你只能替换变量指向的值。但是对于可变对象,你可以进行var1 = {}
、var2 = var1
、var2.update({'another': 'dict'})
这样的操作,并且你会发现var1
也反映了相同的变化。你改变的是可变对象的值,而不是指向它的变量。
在Python中,全局字典不需要使用关键字global来修改它们的值。这是因为当我们使用赋值操作符(=)来修改一个变量时,Python会首先检查该变量是否已经在当前作用域中被定义过。如果该变量在当前作用域中已经存在,Python会将赋值操作视为对该局部变量的修改。然而,如果该变量在当前作用域中还没有被定义过,Python会将赋值操作视为创建一个新的局部变量。
这种行为在修改全局字典时也适用。当我们使用赋值操作符来修改一个全局字典时,Python会首先检查该字典是否在当前作用域中已经被定义过。如果该字典在当前作用域中已经存在,Python会将赋值操作视为对该全局字典的修改。而如果该字典在当前作用域中还没有被定义过,Python会将赋值操作视为创建一个新的局部字典。
然而,对于字典的索引操作是不会存在歧义的。当我们使用索引操作来修改一个字典的值时,Python会默认将其视为对全局字典的修改。因为只有全局字典已经存在,才能执行这个语句而不会抛出错误。
这种行为不仅适用于字典,也适用于列表等其他类型的对象。当我们使用索引操作或者其他可以修改对象的操作时,Python会默认将其视为对全局对象的修改。
这种行为是Python语言的一种设计选择。由于在赋值操作时Python无法确定我们是要修改全局变量还是创建局部变量,所以默认选择创建局部变量。这样做可以避免潜在的歧义和错误。
需要注意的是,如果在当前作用域中同时存在一个局部字典和一个全局字典,那么在修改字典时会优先调用局部字典。
总结起来,全局字典在Python中不需要使用关键字global来修改它们的值。这是因为Python会根据变量是否已经在当前作用域中被定义过来确定是修改全局字典还是创建局部字典。对于修改操作,Python会默认将其视为对全局对象的修改。而对于赋值操作,Python会默认将其视为创建一个新的局部对象。这种设计可以避免潜在的歧义和错误。