在Python中使用globals()的原因?


68

在Python中具有globals()函数的原因是什么?它只返回已经是全局变量的全局变量字典,因此它们可以在任何地方使用...我只是出于好奇而问,试图学习python。

def F():
    global x
    x = 1

def G():
    print(globals()["x"]) #will return value of global 'x', which is 1

def H():
    print(x) #will also return value of global 'x', which, also, is 1

F()
G()
H()

我真的看不到这里的意义吗?只有在我需要局部变量和全局变量时,才需要使用同一个名称

def F():
    global x
    x = 1

def G():
    x = 5
    print(x) #5
    print(globals()["x"]) #1

F()
G()

但是,您永远都不会遇到两个具有相同名称的变量,并且需要在相同范围内使用它们的问题。


3
“但是,您永远不会遇到两个具有相同名称的变量,并且需要在相同范围内使用它们的问题。” 我不能遵循这个想法。这就是单独命名空间的全部原因,因为您在不同的作用域中具有相同名称的变量。当您使用其中两个时,重复的名称是很自然的事,但不是问题,因为您可以给它们加上前缀。
2012年

好吧,我来自C ++家族,所以我不确定Python的人,但是就我所能想到的,您不应该拥有一个没有唯一名称的全局变量。当然,在我看来,同名变量在相同范围内是愚蠢的,但与局部变量同名的全局变量则不会。那是我将命名空间用于本地命名空间的时候……但是,如果您这样说,那就不知道了。

6
@Mahi:globals也许有点误导。globals()本质locals()上是模块的。最接近Python的是globals,它对您所有程序都是真正的全局__builtin__模块。您添加到模块的所有内容将在所有地方的所有命名空间中可用。
马丁·彼得斯

嗯,很好的补充,不是我现在就需要它,但是也许有一天……谢谢:)

Answers:


89

Python为程序员提供了用于反思运行环境的大量工具。globals()只是其中之一,在调试会话中查看全局作用域实际包含哪些对象可能非常有用。

我敢肯定,其背后的原理与locals()用于查看函数中定义的变量或dir用于查看模块的内容或对象的属性的原理相同。

来自C ++背景,我可以理解这些事情似乎是不必要的。在静态链接,静态类型的环境中,它们绝对是。在这种情况下,在编译时确切知道哪些变量是全局变量,对象将拥有哪些成员,甚至是其他编译单元导出的名称。

但是,用动态语言来说,这些事情并不是固定的。它们可以根据导入代码的方式甚至在运行时进行更改。至少由于这个原因,在调试器中访问此类信息可能是无价的。


12
+1。同样,globals可以修改返回的字典(可能是专家最好使用的功能)。Dive Into Python中的引文也与此相关:“使用localsglobals函数,您可以动态获取任意变量的值,以字符串形式提供变量名。这反映了该函数的getattr功能,该功能使您可以通过以下方式动态访问任意函数:以字符串形式提供函数名称。”
史蒂文·鲁姆巴尔斯基2012年

1
并不是说您的回答比别人的回答要好,但对我来说却是如此。谢谢,我想我需要冷静下来,并记住Python不是C ++ :)并感谢所有其他人的回答,这已经很多了。

更改locals()中的元素通常不起作用。大多数python函数没有针对locals的字典-因此,当您调用locals()时,它将构造一个针对locals当前值的字典;更改字典不会反映给当地人。在某些情况下,函数会执行诸如“从xyz导入*”(不再允许)之类的事情-设置在编译时名称未知的局部变量。这些功能是用实际的本地语言字典编译的,很久以前,所有功能都具有实际的本地语言字典,并且它们已经淘汰了它的需要,我不知道您现在有什么条件。
greggo

40

当您需要使用函数的字符串名称调用函数时,它也很有用。例如:

def foo():
    pass

function_name_as_string = 'foo'

globals()[function_name_as_string]() # foo(). 

如果函数具有许多参数,例如def foo(a,b,c = false),该怎么办?您如何将这些参数传递给globals()
Kenan

2
@ksooklall像往常一样将其传递给函数:def foo(*args): print("hw", *args)可以这样做:globals()['foo']()或其他globals()['foo']('reason', 42)
蒂诺

8

你可以传递的结果globals(),并locals()evalexecfile__import__命令。这样做会限制使用这些命令的环境。

因此,这些功能存在以支持其他功能,这些功能得益于被赋予与当前环境可能不同的环境的好处。例如,您可以致电globals()在调用其中一个函数之前先调用然后删除或添加一些变量。


6

globals()很有用eval()-如果您想评估一些在范围内引用变量的代码,这些变量将是全局变量或局部变量。


为了扩展一点,eval()内置函数将解释给它的一串Python代码。签名是:eval(codeString, globals, locals),您可以这样使用它:

def foo():
    x = 2
    y = eval("x + 1", globals(), locals())
    print("y=" + y) # should be 3

之所以行之有效,是因为解释器xlocals()变量字典获取值。您当然可以将自己的变量字典提供给eval。


我不是一个投票否决的人,但是那真的没有道理,您能举一个示例代码以便于理解吗?

当然,我已经扩大了答案。
理查德·

1
Kinda很奇怪,对我来说似乎没用,但这也许只是因为我是Python的新手:P无论如何

eval("x+1")由于其默认参数,其功能相同;看到help(eval)
greggo '16

在这里globals()也可以使用:stackoverflow.com/a/22021058/9024698
弃儿

1

它在“声明性python”中很有用。例如,下面的FooDefBarDef是用于定义一系列数据结构的类,这些数据结构随后将被某些程序包用作其输入或配置。这为您的输入内容提供了很大的灵活性,并且您无需编写解析器。

# FooDef, BarDef are classes

Foo_one = FooDef("This one", opt1 = False, valence = 3 )
Foo_two = FooDef("The other one", valence = 6, parent = Foo_one )

namelist = []
for i in range(6):
    namelist.append("nm%03d"%i)

Foo_other = FooDef("a third one", string_list = namelist )

Bar_thing = BarDef( (Foo_one, Foo_two), method = 'depth-first')

请注意,此配置文件使用循环来建立名称列表,这些名称是 Foo_other。因此,此配置语言带有一个非常强大的“预处理器”以及一个可用的运行时库。如果您想查找复杂的日志,或从zip文件中提取内容并对其进行base64解码,以作为生成配置的一部分(当然,对于输入可能来自于不可靠的来源...)

该软件包使用以下内容读取配置:

conf_globals = {}  # make a namespace
# Give the config file the classes it needs
conf_globals['FooDef']= mypkgconfig.FooDef  # both of these are based ...
conf_globals['BarDef']= mypkgconfig.BarDef  # ... on .DefBase

fname = "user.conf"
try:
    exec open(fname) in conf_globals
except Exception:
    ...as needed...
# now find all the definitions in there
# (I'm assuming the names they are defined with are
# significant to interpreting the data; so they
# are stored under those keys here).

defs = {}
for nm,val in conf_globals.items():
    if isinstance(val,mypkgconfig.DefBase):
        defs[nm] = val

因此,globals()如果您要按程序创建一系列定义,那么最后讲到这一点很有用,当使用这样的程序包时:

for idx in range(20):
    varname = "Foo_%02d" % i
    globals()[varname]= FooDef("one of several", id_code = i+1, scale_ratio = 2**i)

这相当于写出来

Foo_00 = FooDef("one of several", id_code = 1, scale_ratio=1)
Foo_01 = FooDef("one of several", id_code = 2, scale_ratio=2)
Foo_02 = FooDef("one of several", id_code = 3, scale_ratio=4)
... 17 more ...

PLY(Python-lex-yacc)http://www.dabeaz.com/ply/是通过从python模块收集一堆定义来获取输入的软件包的示例 -在这种情况下,对象主要是功能对象,但来自功能对象的元数据(它们的名称,文档字符串和定义顺序)也构成输入的一部分。使用并不是一个很好的例子globals()。而且,它是通过“配置”(后者是普通的python脚本)导入的,而不是相反的方式。

我在一些我从事过的项目中使用了“声明性python”,并且globals()在为这些项目编写配置时使用过。您当然可以辩解说,这是由于配置“语言”的设计方式存在缺陷。用于globals()这种方式不会产生很明显的效果; 只是结果,可能比写出十几个几乎完全相同的语句更容易维护。

您还可以根据变量的名称使用它来赋予变量在配置文件中的重要性:

# All variables above here starting with Foo_k_ are collected
# in Bar_klist
#
foo_k = [ v for k,v in globals().items() if k.startswith('Foo_k_')]
Bar_klist  = BarDef( foo_k , method = "kset")

此方法对于定义了许多表和结构的任何python模块可能很有用,从而可以更轻松地向数据中添加项目,而无需维护引用。


0

它也可以用来从字符串中获取类'classname'的实例:

class C:
    def __init__(self, x):
        self.x = x
        print('Added new instance, x:', self.x)


def call(str):
    obj = globals()[str](4)
    return obj

c = call('C')
print(c.x)

0

如果您想导入刚刚构建的模块,这可能会很有用:

py

[...]

def buildModule():
    [...code to build module...]
    return __import__("somemodule")

[...]

b.py

from a import buildModule

def setup():
   globals()["somemodule"] = buildModule()

0

并不是的。全局变量Python实际上具有的是模块范围的变量。

# a.py
print(globals())

import b
b.tt()
# b.py
def tt():
    print(globals())

运行python a.py,至少有两个输出globals()['__name__']是不同的。

代码这里在Github上CPython的显示它。

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.