题 在函数中使用全局变量


如何在函数中创建或使用全局变量?

如果我在一个函数中创建一个全局变量,我如何在另一个函数中使用该全局变量?我是否需要将全局变量存储在需要访问的函数的局部变量中?


2478
2018-01-08 05:45


起源




答案:


您可以通过将其声明为其他函数来使用全局变量 global 在分配给它的每个函数中:

globvar = 0

def set_globvar_to_one():
    global globvar    # Needed to modify global copy of globvar
    globvar = 1

def print_globvar():
    print(globvar)     # No need for global declaration to read value of globvar

set_globvar_to_one()
print_globvar()       # Prints 1

我想它的原因是,因为全局变量是如此危险,所以Python希望通过明确要求你确实知道你正在玩的是什么 global 关键词。

如果要跨模块共享全局变量,请参阅其他答案。


3509
2018-01-08 08:39



将全局变量称为“如此危险”是极其夸张的。 Globals在所有曾经存在且永远存在的语言中都是完美的。他们有自己的位置。您应该说的是,如果您不知道如何编程,它们可能会导致问题。 - Anthony
我认为他们相当危险。但是在python中,“全局”变量实际上是模块级的,这解决了很多问题。 - Fábio Santos
我不同意Python需要的原因 global 关键字是因为全局变量是危险的。相反,这是因为语言不要求您显式声明变量并自动假定您指定的变量具有函数作用域,除非您另有说明。该 global 关键字是提供给它的方式。 - Nate C-K
全局变量是一种设计气味,几乎是我专业使用的每种语言。假设您有一个需要三个布尔值的函数:这​​是要检查的8个可能的代码路径。如果你有3个具有相同布尔args数的函数,那就是24个可能的代码路径。然后添加一个布尔全局变量,现在您正在查看24 ^ 2个可能的代码路径,因为您必须考虑到所有函数都能够更改该全局变量的状态这一事实。这就是功能编程技术变得流行的原因。 - avgvstvs
@LightnessRacesinOrbit我不明白你的观点。如果删除全局变量,则现在删除复杂因素, 任意函数不能再改变程序状态,在执行中的各个点 - 从而以依赖于该变量的其他函数无法察觉的方式改变执行。你不再需要跟踪,“没有 f2() 现在改变状态 f3() 可能做出意想不到的事这些功能现在可以与外部程序状态无关。 - avgvstvs


如果我正确理解您的情况,您所看到的是Python处理本地(函数)和全局(模块)命名空间的结果。

假设你有一个这样的模块:

# sample.py
myGlobal = 5

def func1():
    myGlobal = 42

def func2():
    print myGlobal

func1()
func2()

您可能希望这打印42,但它打印5.正如已经提到的,如果你添加'global'宣言 func1(), 然后 func2() 将打印42。

def func1():
    global myGlobal
    myGlobal = 42

这里发生的是Python假设任何名称 分配给除非另有明确说明,否则函数内的任何位置都是该函数的本地函数。如果只是  从名称开始,名称在本地不存在,它将尝试在任何包含范围内查找名称(例如模块的全局范围)。

为名称分配42时 myGlobal因此,Python创建了一个局部变量,它隐藏了同名的全局变量。那个本地超出范围而且是 垃圾回收 什么时候 func1() 回报;与此同时, func2() 除了(未修改的)全局名称之外,什么都看不到。请注意,此命名空间决策发生在编译时,而不是在运行时 - 如果您要读取它的值 myGlobal 内 func1() 在你分配它之前,你会得到一个 UnboundLocalError,因为Python已经决定它必须是一个局部变量,但它还没有任何与之相关的值。但通过使用'global'声明,你告诉Python它应该在别处查找名称,而不是在本地分配它。

(我相信这种行为很大程度上是由于本地命名空间的优化 - 没有这种行为,每次在函数内部分配新名称时,Python的VM都需要执行至少三次名称查找(以确保名称没有' t已经存在于模块/内置级别),这将显着减慢非常常见的操作。)


661
2018-01-08 09:19



您提到命名空间决定发生在 编译时间,我不认为这是真的。从我学习python的编译 只检查语法错误,而不是名称错误 试试这个例子 def A():x + = 1,如果你不运行它,它会 不要给UnboundLocalError,请核实谢谢 - watashiSHUN
通常使用大写字母表示全局变量 MyGlobal = 5 - Vassilis
@watashiSHUN:命名空间决定 不 发生在编译时。决定那个 x 本地与在运行时检查不同,如果本地名称在第一次使用之前绑定到某个值。 - BlackJack
@Vassilis:大写是常见的 所有 信件: MY_GLOBAL = 5。见 Python代码的样式指南。 - BlackJack
@BlackJack,是的,你是对的! (我习惯了) - Vassilis


你可能想探索一下这个概念 命名空间。在Python中,  是自然的地方 全球 数据:

每个模块都有自己的私有符号表,该表用作模块中定义的所有函数的全局符号表。因此,模块的作者可以在模块中使用全局变量,而不必担心与用户的全局变量的意外冲突。另一方面,如果您知道自己在做什么,可以使用与其函数相同的符号来触摸模块的全局变量, modname.itemname

此处描述了全局模块的特定用法 - 怎么办,我共享全局变量 - 跨模块,为了完整性,内容在这里共享:

在单个程序中跨模块共享信息的规范方法是创建一个特殊的配置模块(通常称为config或cfg)。只需在应用程序的所有模块中导入配置模块;然后该模块可用作全局名称。因为每个模块只有一个实例,所以对模块对象所做的任何更改都会在任何地方反映出来。例如:

文件:config.py

x = 0   # Default value of the 'x' configuration setting

文件:mod.py

import config
config.x = 1

文件:main.py

import config
import mod
print config.x

175
2018-01-08 05:59



因为我不喜欢的原因 config.x  我可以摆脱它吗?我来了 x = lambda: config.x 然后我有了 新 价值 x()。由于某种原因,有 a = config.x 对我来说不起作用。 - vladosaurus


Python使用一个简单的启发式方法来决定它应该在本地和全局之间加载变量的范围。如果变量名称出现在赋值的左侧,但未声明为全局变量,则假定它是本地的。如果它没有出现在作业的左侧,则假定它是全局的。

>>> import dis
>>> def foo():
...     global bar
...     baz = 5
...     print bar
...     print baz
...     print quux
... 
>>> dis.disassemble(foo.func_code)
  3           0 LOAD_CONST               1 (5)
              3 STORE_FAST               0 (baz)

  4           6 LOAD_GLOBAL              0 (bar)
              9 PRINT_ITEM          
             10 PRINT_NEWLINE       

  5          11 LOAD_FAST                0 (baz)
             14 PRINT_ITEM          
             15 PRINT_NEWLINE       

  6          16 LOAD_GLOBAL              1 (quux)
             19 PRINT_ITEM          
             20 PRINT_NEWLINE       
             21 LOAD_CONST               0 (None)
             24 RETURN_VALUE        
>>> 

看看baz是如何出现在作业的左侧的 foo(), 是唯一的 LOAD_FAST 变量。


75
2017-07-12 12:35



启发式寻找 绑定操作。分配是一个这样的操作,导入另一个。但是目标是 for 循环和之后的名称 as 在 with 和 except 陈述也是必然的。 - Martijn Pieters♦


如果要在函数中引用全局变量,可以使用 全球 用于声明哪些变量是全局变量的关键字。您不必在所有情况下都使用它(因为此处有人错误地声称) - 如果表达式中引用的名称无法在定义此函数的函数的本地范围或范围中找到,则在全局范围内查找变量。

但是,如果分配给函数中未声明为全局的新变量,则它将隐式声明为local,并且它可以掩盖具有相同名称的任何现有全局变量。

此外,全局变量是有用的,与一些声称不同的OOP狂热者相反 - 特别是对于较小的脚本,其中OOP是过度的。


48
2018-01-08 09:03





除了已经存在的答案,并使这更令人困惑:

在Python中,仅在函数内引用的变量是    隐含全球性。如果在任何地方为变量分配了新值   在函数体内,它被认为是一个 本地。如果是变量   在函数内部分配了一个新值,变量是   隐式本地,您需要明确地将其声明为“全局”。

虽然起初有点令人惊讶,但片刻的考虑解释了   这个。一方面,要求全局分配变量提供了一个   禁止意外的副作用。另一方面,如果全球化了   所有全局引用都需要,你将全局使用全局   时间。您必须声明为内置的每个引用的全局   功能或导入模块的组件。这种混乱会   挫败全球宣言的有用性   副作用。

资源: Python中的局部变量和全局变量有哪些规则?


36
2017-07-04 10:23





如果我在一个函数中创建一个全局变量,我如何在另一个函数中使用该变量?

我们可以使用以下函数创建一个全局:

def create_global_variable():
    global global_variable # must declare it to be a global first
    # modifications are thus reflected on the module's global scope
    global_variable = 'Foo' 

编写函数实际上并不运行其代码。所以我们称之为 create_global_variable 功能:

>>> create_global_variable()

使用全局变量而无需修改

您可以使用它,只要您不希望更改它指向的对象:

例如,

def use_global_variable():
    return global_variable + '!!!'

现在我们可以使用全局变量:

>>> use_global_variable()
'Foo!!!'

从函数内部修改全局变量

要将全局变量指向其他对象,您需要再次使用global关键字:

def change_global_variable():
    global global_variable
    global_variable = 'Bar'

请注意,在编写此函数后,实际更改它的代码仍未运行:

>>> use_global_variable()
'Foo!!!'

所以在调用函数后:

>>> change_global_variable()

我们可以看到全局变量已经改变。该 global_variable 名字现在指向 'Bar'

>>> use_global_variable()
'Bar!!!'

请注意,Python中的“全局”并不是真正的全局 - 它只是模块级别的全局。因此它仅适用于在全局模块中编写的函数。函数会记住编写它们的模块,因此当它们导出到其他模块时,它们仍会查看创建它们的模块以查找全局变量。

具有相同名称的局部变量

如果您创建一个具有相同名称的局部变量,它将掩盖一个全局变量:

def use_local_with_same_name_as_global():
    # bad name for a local variable, though.
    global_variable = 'Baz' 
    return global_variable + '!!!'

>>> use_local_with_same_name_as_global()
'Baz!!!'

但是使用那个错误命名的局部变量不会改变全局变量:

>>> use_global_variable()
'Bar!!!'

请注意,除非您确切知道自己在做什么并且有充分的理由这样做,否则应该避免使用与globals同名的局部变量。我还没有遇到过这样的道理。


31
2018-01-01 19:55





对于并行执行,如果您不了解发生的情况,全局变量可能会导致意外结果。以下是在多处理中使用全局变量的示例。我们可以清楚地看到每个进程都使用自己的变量副本:

import multiprocessing
import os
import random
import sys
import time

def worker(new_value):
    old_value = get_value()
    set_value(random.randint(1, 99))
    print('pid=[{pid}] '
          'old_value=[{old_value:2}] '
          'new_value=[{new_value:2}] '
          'get_value=[{get_value:2}]'.format(
          pid=str(os.getpid()),
          old_value=old_value,
          new_value=new_value,
          get_value=get_value()))

def get_value():
    global global_variable
    return global_variable

def set_value(new_value):
    global global_variable
    global_variable = new_value

global_variable = -1

print('before set_value(), get_value() = [%s]' % get_value())
set_value(new_value=-2)
print('after  set_value(), get_value() = [%s]' % get_value())

processPool = multiprocessing.Pool(processes=5)
processPool.map(func=worker, iterable=range(15))

输出:

before set_value(), get_value() = [-1]
after  set_value(), get_value() = [-2]
pid=[53970] old_value=[-2] new_value=[ 0] get_value=[23]
pid=[53971] old_value=[-2] new_value=[ 1] get_value=[42]
pid=[53970] old_value=[23] new_value=[ 4] get_value=[50]
pid=[53970] old_value=[50] new_value=[ 6] get_value=[14]
pid=[53971] old_value=[42] new_value=[ 5] get_value=[31]
pid=[53972] old_value=[-2] new_value=[ 2] get_value=[44]
pid=[53973] old_value=[-2] new_value=[ 3] get_value=[94]
pid=[53970] old_value=[14] new_value=[ 7] get_value=[21]
pid=[53971] old_value=[31] new_value=[ 8] get_value=[34]
pid=[53972] old_value=[44] new_value=[ 9] get_value=[59]
pid=[53973] old_value=[94] new_value=[10] get_value=[87]
pid=[53970] old_value=[21] new_value=[11] get_value=[21]
pid=[53971] old_value=[34] new_value=[12] get_value=[82]
pid=[53972] old_value=[59] new_value=[13] get_value=[ 4]
pid=[53973] old_value=[87] new_value=[14] get_value=[70]

28
2017-10-03 05:41