全局变量:赋值vs方法调用



为了避免意外修改全局变量,python需要在给全局变量赋值之前显式地使用global语句。但是,通过调用全局变量的方法来修改全局变量可以不需要任何额外的语句:

x = [1, 2]
y = [1, 2]
def f():
  global x
  x = x + [3] # won't affect global variable without global statement
  y.append(3) # will affect global variable without global statement

这似乎有点不一致。与用一个全新的对象替换全局可变对象相比,通过方法调用修改全局可变对象是否更危险/更不坏?如果是,为什么?

来自文档:

在Python中,只在函数内部引用的变量是隐式全球如果一个变量在任何地方赋值在函数体中,被假定为局部unless

在您的示例中,y在函数内部被引用,因此是隐式全局的。另一方面,x被赋值,因此除非另有显式声明,否则它必须是局部的。

文档进一步回答了你的问题:

虽然一开始有点惊讶,但考虑一下就解释了这一点。一方面,为赋值变量要求全局,提供了一个防止意外的副作用。另一方面,if global was对于所有全局引用都是必需的,您将使用全局的时间。必须将每个对内置对象的引用声明为全局函数或导入模块的组件。这些杂乱的东西破坏全球声明对识别的有用性副作用。

这不是关于值级别的可变性,这是你看待它的方式;它是关于变量引用的可变性,即一个命名项(变量)指向什么。

x = [1, 2]
print(id(x)) # 57226944
y = [1, 2]
print(id(y)) # 57262728
def f():
  global x
  x = x + [3]
  print(id(x)) # 57306648 - CHANGED
  y.append(3)
  print(id(y)) # 57262728 - UNCHANGED
f()

请注意,名称'x'现在指向一个新的东西(一个新创建的列表),而对y的.append操作并没有改变名称'y'指向的内容。

一句话:

"如果不显式声明,python将不允许您更改全局变量的reference "。

现在让我们来解释一下你刚刚读到的内容,对象对变量的赋值实际上是创建一个reference,这是一个内存地址,指向对象在内存中的位置。

当我们写

x = [1, 2]

实际发生的情况是,在内存的某个地方,列表对象被分配了所有的函数引用、成员和其他东西。这个地址是实际保存在x中的。

我们可以使用id(object)函数来注意这个变化:

x = [1, 2]
def foo():
 print id(x)    # an address like 50075016
 y = [1, 2, 3]
 print id(y)    # another address like 50075272 
 x = y          # won't work without declaring 'global x' 
                # because we try to change the address stored in x
                # from 50075016 to 50075272.
 x.append(3)    # works
 print id(x)    # same address 50075016

最新更新