在函数中使用全局变量


3112

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

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

Answers:


4243

您可以在其他函数中使用全局变量,方法是像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关键字来确保您真正知道这就是要使用的内容。

如果要在模块之间共享全局变量,请参见其他答案。


838
将全局变量称为“如此危险”是极端夸张的。全球语言在曾经存在和将要存在的每种语言中都非常出色。他们有自己的位置。您应该说的是,如果您不知道如何编程,它们可能会引起问题。
安东尼

207
我认为它们非常危险。但是在python中,“全局”变量实际上是模块级的,这解决了很多问题。
法比奥·桑托斯

246
我不同意Python需要该global关键字的原因是因为全局变量很危险。而是因为该语言不需要您显式声明变量,而是自动假定您分配的变量具有函数作用域,除非另行说明。该global关键字是提供以其他方式告诉它的手段。
Nate CK

7
@avgvstvs:而且,如果您要在不使用全局变量的情况下实现相同的程序,那么您仍然具有相同数量的代码路径。您提出的论点并非反对全球主义者。
Lightness Races in Orbit

13
@LightnessRacesinOrbit我不明白你的意思。如果删除全局变量,那么现在就删除了复杂的因素,因为任意函数不再可以在执行的各个点更改程序状态,从而以其他方式无法依靠依赖该变量的其他函数来更改执行。您不再需要跟踪“ f2()更改状态,因此现在f3()可能会发生意外的事情吗?这些功能现在可以与外部程序状态无关
。– avgvstvs

775

如果我正确地理解了您的情况,那么您所看到的是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假定在函数内的任何位置分配给的任何名称都是该函数的本地名称,除非另有明确说明。如果仅从名称读取,并且该名称在本地不存在,它将尝试在任何包含的作用域(例如,模块的全局作用域)中查找该名称。

myGlobal因此,当您为name分配42时,Python将创建一个局部变量,该局部变量遮盖同名的全局变量。该局部变量超出范围并在返回时被垃圾回收func1();同时,func2()除了(未修改的)全局名称外,再也看不到其他任何内容。请注意,此名称空间决定是在编译时发生的,而不是在运行时发生的-如果在分配它之前先读取myGlobalinside 的值func1(),则会得到一个UnboundLocalError,因为Python已经确定它必须是局部变量,但它尚未具有任何关联的价值。但是通过使用' global'语句,您告诉Python应该在其他地方查找该名称,而不是在本地分配它。

(我相信这种行为主要是通过优化本地名称空间而引起的-如果没有这种行为,Python的VM每次在函数内部分配新名称时都需要至少执行三个名称查找(以确保名称没有t已存在于模块/内置级别),这会大大减慢非常常见的操作的速度。)


您提到了命名空间决定是在编译时发生的,我认为这是不正确的。从我学到的内容来看,python的编译仅检查语法错误,而不检查名称错误,请尝试以下示例def A():x + = 1,如果不运行它,它将不会给出UnboundLocalError,请验证谢谢
watashiSHUN

1
通常对全局变量使用大写字母,例如MyGlobal = 5
Vassilis

3
@watashiSHUN:名称空间决定确实在编译时发生。确定x本地名称不同于在运行时检查本地名称是否在首次使用之前已绑定到某个值。
BlackJack

9
@Vassilis:通常所有字母大写:MY_GLOBAL = 5。请参见Python代码样式指南
BlackJack

223

您可能想探索名称空间的概念。在Python中,该模块全局数据的自然存放位置:

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

此处描述了模块中全局变量的特定用法- 如何在模块之间共享全局变量?,为了完整起见,这里共享内容:

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

文件: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

1
由于某种原因,我不喜欢config.x 可以摆脱它?我来了x = lambda: config.x,然后在中有了新的价值x()。由于某种原因,拥有a = config.x对我没有帮助。
vladosaurus

3
@vladosaurus from config import x解决了吗?
jhylands '18

93

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变量。


12
启发式寻找绑定操作。分配就是这样一种操作,而导入是另一种。但是for循环的目标和asin withexcept语句之后的名称也被绑定。
马丁·彼得斯

@MartijnPieters对于名称后as的中except条款,这不是明显对我。但是它会被自动删除以节省内存。
罗伯特

1
@Robert:不是节省内存,而是避免创建循环引用,否则可能导致内存泄漏。这是因为异常引用了回溯,并且回溯引用了整个调用堆栈中的每个本地和全局名称空间,包括as ...异常处理程序中的目标。
马丁·彼得斯

62

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

但是,如果分配给未在函数中声明为全局变量的新变量,则该变量将隐式声明为局部变量,并且可能使任何现有的具有相同名称的全局变量都模糊不清。

同样,全局变量是有用的,这与某些OOP狂热者相反,特别是对于较小的脚本(OOP过于杀伤)更是如此。


绝对是。狂热者。大多数Python用户使用它进行脚本编写,并创建一些小功能来分离出少量代码。
保罗·乌萨克

51

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

我们可以使用以下功能创建一个全局变量:

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!!!'

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

我们在课堂上得到相同的行为

后面有评论问:

如果我想在一个类内的函数内创建一个全局变量,并想在另一个类内的另一个函数内使用该变量,该怎么办?

在这里,我演示了我们在方法中的行为与常规函数中的行为相同:

class Foo:
    def foo(self):
        global global_variable
        global_variable = 'Foo'

class Bar:
    def bar(self):
        return global_variable + '!!!'

Foo().foo()

现在:

>>> Bar().bar()
'Foo!!!'

但是我建议不要使用全局变量,而应使用类属性,以避免使模块名称空间混乱。还要注意,我们self在这里不使用参数-这些可以是类方法(如果从常规cls参数中更改class属性,则很方便)或静态方法(no selfcls)。


很酷,但是如果我想在一个类内的函数内创建一个全局变量,并想在另一个类内的另一个函数内使用该变量,该怎么办?
金达卡

2
@anonmanx我不知道为什么会被卡住,它在方法中的行为与常规函数中的行为相同。但是我会用您的评论和一些演示代码来更新我的答案,好吗?
亚伦·霍尔

1
@anonmanx怎么样?
亚伦·霍尔

好,知道了。因此,我将必须显式调用该函数以使用该全局变量。
anonmanx

47

除了已经存在的答案之外,还要使其更加混乱:

在Python中,仅在函数内部引用的变量是 隐式全局的。如果在函数体内的任何位置为变量分配了新值,则假定该变量为local。如果在函数内部为变量分配了新值,则该变量是隐式局部变量,您需要将其显式声明为“ global”。

尽管起初有些令人惊讶,但片刻的考虑可以解释这一点。一方面,要求全局分配变量可防止意外副作用。另一方面,如果所有全局引用都需要全局,那么您将一直使用全局。您必须将对内置函数或导入模块的组件的每个引用声明为全局引用。这种混乱将破坏全球宣言对确定副作用的有用性。

资料来源:Python中局部和全局变量的规则是什么?


34

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

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]

25

您所说的是使用这样的方法:

globvar = 5

def f():
    var = globvar
    print(var)

f()  # Prints 5

但是更好的方法是像这样使用全局变量:

globavar = 5
def f():
    global globvar
    print(globvar)
f()   #prints 5

两者给出相同的输出。


25

事实证明,答案总是很简单。

这是一个小示例模块,具有在main定义中显示它的简单方法:

def five(enterAnumber,sumation):
    global helper
    helper  = enterAnumber + sumation

def isTheNumber():
    return helper

这是如何在main定义中显示它:

import TestPy

def main():
    atest  = TestPy
    atest.five(5,8)
    print(atest.isTheNumber())

if __name__ == '__main__':
    main()

这个简单的代码就是这样,它将执行。希望对您有所帮助。


1
谢谢,我是python的新手,但是懂一点Java。你说的对我有用。并在类中编写global a <ENTER> ..对我来说,比在编写'global a'中编写函数更有意义。.我注意到您不能说global a = 4
barlop

2
对我来说,这可能是最简单但非常有用的python技巧。我将该模块命名为global_vars,并在中初始化init_global_vars在启动脚本中调用的数据。然后,我只需为每个定义的全局变量创建访问器方法。希望我可以多次投票!谢谢彼得!
swdev 2014年

1
如果有许多全局变量,而又不想在全局语句后一一列出,该怎么办?
jtlz2

23

您需要在要使用的每个函数中引用全局变量。

如下:

var = "test"

def printGlobalText():
    global var #wWe are telling to explicitly use the global version
    var = "global from printGlobalText fun."
    print "var from printGlobalText: " + var

def printLocalText():
    #We are NOT telling to explicitly use the global version, so we are creating a local variable
    var = "local version from printLocalText fun"
    print "var from printLocalText: " + var

printGlobalText()
printLocalText()
"""
Output Result:
var from printGlobalText: global from printGlobalText fun.
var from printLocalText: local version from printLocalText
[Finished in 0.1s]
"""

3
“在您要使用的每个功能中”都是错误的,应该更接近:“在您要更新的每个功能中”
spazm 2015年

21

您实际上并没有将全局变量存储在本地变量中,而只是创建了对原始全局引用所引用的同一对象的本地引用。请记住,Python中的几乎所有内容都是一个引用对象的名称,在常规操作中不会复制任何内容。

如果不必显式指定标识符何时引用预定义的全局变量,则可能必须显式指定何时标识符是新的局部变量(例如,使用类似“ var”命令的东西)在JavaScript中看到)。由于局部变量在任何严重且不平凡的系统中都比全局变量更普遍,因此在大多数情况下,Python的系统更为有意义。

可能有一种尝试进行猜测的语言,如果存在则使用全局变量,如果不存在则创建本地变量。但是,这很容易出错。例如,导入另一个模块可能会无意中通过该名称引入全局变量,从而改变程序的行为。




14

接下来,作为附加,使用文件包含所有在本地声明的所有全局变量,然后import as

文件initval.py

Stocksin = 300
Prices = []

文件getstocks.py

import initval as iv

def getmystocks(): 
    iv.Stocksin = getstockcount()


def getmycharts():
    for ic in range(iv.Stocksin):

1
将全局变量移动到另一个文件有什么好处?只是将全局变量组合到一个小文件中吗?为什么使用该语句import ... as ...?为什么不只是import ...呢?
olibre

1
啊...我终于明白了优点:无需使用关键字global:-) => +1 :-)请编辑您的答案以阐明其他人可能也有的这些疑问。欢呼声
奥利伯(Olibre)'17

13

写入全局数组的显式元素显然不需要全局声明,尽管对其进行“批发”确实具有该要求:

import numpy as np

hostValue = 3.14159
hostArray = np.array([2., 3.])
hostMatrix = np.array([[1.0, 0.0],[ 0.0, 1.0]])

def func1():
    global hostValue    # mandatory, else local.
    hostValue = 2.0

def func2():
    global hostValue    # mandatory, else UnboundLocalError.
    hostValue += 1.0

def func3():
    global hostArray    # mandatory, else local.
    hostArray = np.array([14., 15.])

def func4():            # no need for globals
    hostArray[0] = 123.4

def func5():            # no need for globals
    hostArray[1] += 1.0

def func6():            # no need for globals
    hostMatrix[1][1] = 12.

def func7():            # no need for globals
    hostMatrix[0][0] += 0.33

func1()
print "After func1(), hostValue = ", hostValue
func2()
print "After func2(), hostValue = ", hostValue
func3()
print "After func3(), hostArray = ", hostArray
func4()
print "After func4(), hostArray = ", hostArray
func5()
print "After func5(), hostArray = ", hostArray
func6()
print "After func6(), hostMatrix = \n", hostMatrix
func7()
print "After func7(), hostMatrix = \n", hostMatrix

7

我添加此内容是因为我在其他任何答案中都没有看到它,这对于那些在类似问题上苦苦挣扎的人可能很有用。该globals()函数返回一个可变的全局符号字典,您可以在其中“神奇地”使数据可用于其余代码。例如:

from pickle import load
def loaditem(name):
    with open(r"C:\pickle\file\location"+"\{}.dat".format(name), "rb") as openfile:
        globals()[name] = load(openfile)
    return True

from pickle import dump
def dumpfile(name):
    with open(name+".dat", "wb") as outfile:
        dump(globals()[name], outfile)
    return True

只会让您将变量转储/加载到全局名称空间中。超级方便,没有混乱,没有大惊小怪。可以肯定,它仅适用于Python 3。


3
globals()总是返回在局部上下文中可用的全局变量,因此此处的突变可能不会反映在另一个模块中。
基兰·乔纳拉达达

6

引用要在其中显示更改的类名称空间。

在此示例中,Runner正在使用文件配置中的max。我希望我的测试在跑步者使用它时更改max的值。

main / config.py

max = 15000

main / runner.py

from main import config
def check_threads():
    return max < thread_count 

测试/runner_test.py

from main import runner                # <----- 1. add file
from main.runner import check_threads
class RunnerTest(unittest):
   def test_threads(self):
       runner.max = 0                  # <----- 2. set global 
       check_threads()

1

全局变量很好-多重处理除外

与不同平台/环境的多处理相关的全局问题(一方面是Windows / Mac OS,另一方面是Linux)很麻烦。

我将通过一个简单的示例向您展示这个问题,该问题指出了我前一段时间遇到的问题。

如果您想了解Windows / MacO和Linux上为何有所不同的原因,那么您需要知道这是在...上启动新进程的默认机制。

  • Windows / MacO是“生成”的
  • Linux是“ fork”

它们在内存分配和初始化方面有所不同...(但在此不做介绍)。

让我们看一下问题/示例...

import multiprocessing

counter = 0

def do(task_id):
    global counter
    counter +=1
    print(f'task {task_id}: counter = {counter}')

if __name__ == '__main__':

    pool = multiprocessing.Pool(processes=4)
    task_ids = list(range(4))
    pool.map(do, task_ids)

视窗

如果您在Windows上运行此程序(我也想在MacOS上运行),则会得到以下输出...

task 0: counter = 1
task 1: counter = 2
task 2: counter = 3
task 3: counter = 4

的Linux

如果在Linux上运行它,则会得到以下内容。

task 0: counter = 1
task 1: counter = 1
task 2: counter = 1
task 3: counter = 1
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.