简体   繁体   English

导入模块中全局变量的可见性

[英]Visibility of global variables in imported modules

I've run into a bit of a wall importing modules in a Python script.我在 Python 脚本中遇到了一些导入模块的问题。 I'll do my best to describe the error, why I run into it, and why I'm tying this particular approach to solve my problem (which I will describe in a second):我会尽力描述错误,为什么会遇到它,以及为什么我要使用这种特殊的方法来解决我的问题(我将在稍后描述):

Let's suppose I have a module in which I've defined some utility functions/classes, which refer to entities defined in the namespace into which this auxiliary module will be imported (let "a" be such an entity):假设我有一个模块,我在其中定义了一些实用函数/类,它们引用了将要导入该辅助模块的命名空间中定义的实体(让“a”成为这样的实体):

module1:模块1:

def f():
    print a

And then I have the main program, where "a" is defined, into which I want to import those utilities:然后我有主程序,其中定义了“a”,我想将这些实用程序导入其中:

import module1
a=3
module1.f()

Executing the program will trigger the following error:执行程序会触发如下错误:

Traceback (most recent call last):
  File "Z:\Python\main.py", line 10, in <module>
    module1.f()
  File "Z:\Python\module1.py", line 3, in f
    print a
NameError: global name 'a' is not defined

Similar questions have been asked in the past (two days ago, d'uh) and several solutions have been suggested, however I don't really think these fit my requirements.过去(两天前,d'uh) 也提出了类似的问题,并提出了几种解决方案,但我认为这些并不符合我的要求。 Here's my particular context:这是我的特定背景:

I'm trying to make a Python program which connects to a MySQL database server and displays/modifies data with a GUI.我正在尝试制作一个 Python 程序,它连接到 MySQL 数据库服务器并使用 GUI 显示/修改数据。 For cleanliness sake, I've defined the bunch of auxiliary/utility MySQL-related functions in a separate file.为了简洁起见,我在一个单独的文件中定义了一堆与 MySQL 相关的辅助/实用程序函数。 However they all have a common variable, which I had originally defined inside the utilities module, and which is the cursor object from MySQLdb module.但是它们都有一个公共变量,我最初实用程序模块中定义,它是 MySQLdb 模块中的游标对象。 I later realised that the cursor object (which is used to communicate with the db server) should be defined in the main module, so that both the main module and anything that is imported into it can access that object.后来我意识到应该在主模块中定义游标对象(用于与数据库服务器通信),以便主模块和导入到其中的任何内容都可以访问该对象。

End result would be something like this:最终结果将是这样的:

utilities_module.py:实用程序_module.py:

def utility_1(args):
    code which references a variable named "cur"
def utility_n(args):
    etcetera

And my main module:我的主要模块:

program.py:程序.py:

import MySQLdb, Tkinter
db=MySQLdb.connect(#blahblah) ; cur=db.cursor()  #cur is defined!
from utilities_module import *

And then, as soon as I try to call any of the utilities functions, it triggers the aforementioned "global name not defined" error.然后,一旦我尝试调用任何实用程序函数,它就会触发上述“未定义全局名称”错误。

A particular suggestion was to have a "from program import cur" statement in the utilities file, such as this:一个特别的建议是在实用程序文件中有一个“from program import cur”语句,例如:

utilities_module.py:实用程序_module.py:

from program import cur
#rest of function definitions

program.py:程序.py:

import Tkinter, MySQLdb
db=MySQLdb.connect(#blahblah) ; cur=db.cursor()  #cur is defined!
from utilities_module import *

But that's cyclic import or something like that and, bottom line, it crashes too.但这是循环导入或类似的东西,最重要的是,它也会崩溃。 So my question is:所以我的问题是:

How in hell can I make the "cur" object, defined in the main module, visible to those auxiliary functions which are imported into it?我怎么能让主模块中定义的“cur”对象对导入到其中的辅助函数可见?

Thanks for your time and my deepest apologies if the solution has been posted elsewhere.感谢您的宝贵时间,如果解决方案已在其他地方发布,我深表歉意。 I just can't find the answer myself and I've got no more tricks in my book.我自己找不到答案,而且我的书中没有更多的技巧。

Globals in Python are global to a module , not across all modules. Python 中的全局变量对于一个模块来说是全局的,而不是在所有模块中。 (Many people are confused by this, because in, say, C, a global is the same across all implementation files unless you explicitly make it static .) (很多人对此感到困惑,因为在 C 语言中,全局在所有实现文件中都是相同的,除非您明确将其static 。)

There are different ways to solve this, depending on your actual use case.有不同的方法可以解决这个问题,具体取决于您的实际用例。


Before even going down this path, ask yourself whether this really needs to be global.在走这条路之前,问问自己这是否真的需要全球化。 Maybe you really want a class, with f as an instance method, rather than just a free function?也许你真的想要一个以f作为实例方法的类,而不仅仅是一个自由函数? Then you could do something like this:然后你可以做这样的事情:

import module1
thingy1 = module1.Thingy(a=3)
thingy1.f()

If you really do want a global, but it's just there to be used by module1 , set it in that module.如果您确实想要一个全局变量,但它只是供module1使用,请将其设置在该模块中。

import module1
module1.a=3
module1.f()

On the other hand, if a is shared by a whole lot of modules, put it somewhere else, and have everyone import it:另一方面,如果a被很多模块共享,请将其放在其他地方,并让每个人都导入它:

import shared_stuff
import module1
shared_stuff.a = 3
module1.f()

… and, in module1.py: …并且,在module1.py中:

import shared_stuff
def f():
    print shared_stuff.a

Don't use a from import unless the variable is intended to be a constant.不要使用from导入,除非该变量是常量。 from shared_stuff import a would create a new a variable initialized to whatever shared_stuff.a referred to at the time of the import, and this new a variable would not be affected by assignments to shared_stuff.a . from shared_stuff import a将创建一个新a变量,该变量初始化为导入时引用的任何shared_stuff.a ,并且这个新a变量不会受到对shared_stuff.a的分配的影响。


Or, in the rare case that you really do need it to be truly global everywhere, like a builtin, add it to the builtin module.或者,在极少数情况下,您确实需要它在任何地方都是真正的全局,例如内置,将其添加到内置模块中。 The exact details differ between Python 2.x and 3.x. Python 2.x 和 3.x 之间的确切细节有所不同。 In 3.x, it works like this:在 3.x 中,它的工作方式如下:

import builtins
import module1
builtins.a = 3
module1.f()

As a workaround, you could consider setting environment variables in the outer layer, like this.作为一种解决方法,您可以考虑在外层设置环境变量,如下所示。

main.py:主要.py:

import os
os.environ['MYVAL'] = str(myintvariable)

mymodule.py:我的模块.py:

import os

myval = None
if 'MYVAL' in os.environ:
    myval = os.environ['MYVAL']

As an extra precaution, handle the case when MYVAL is not defined inside the module.作为额外的预防措施,处理模块内部未定义 MYVAL 的情况。

A function uses the globals of the module it's defined in. Instead of setting a = 3 , for example, you should be setting module1.a = 3 .函数使用定义它的模块的全局变量。例如,您应该设置module1.a = 3而不是设置a = 3 = 3 。 So, if you want cur available as a global in utilities_module , set utilities_module.cur .因此,如果您希望curutilities_module模块中作为全局变量使用,请设置utilities_module.cur模块.cur。

A better solution: don't use globals.更好的解决方案:不要使用全局变量。 Pass the variables you need into the functions that need it, or create a class to bundle all the data together, and pass it when initializing the instance.将你需要的变量传递给需要它的函数,或者创建一个类将所有数据捆绑在一起,并在初始化实例时传递。

This post is just an observation for Python behaviour I encountered.这篇文章只是对我遇到的 Python 行为的观察。 Maybe the advices you read above don't work for you if you made the same thing I did below.如果您做了与我在下面所做的相同的事情,那么您在上面阅读的建议可能对您不起作用。

Namely, I have a module which contains global/shared variables (as suggested above):也就是说,我有一个包含全局/共享变量的模块(如上所述):

#sharedstuff.py

globaltimes_randomnode=[]
globalist_randomnode=[]

Then I had the main module which imports the shared stuff with:然后我有一个导入共享内容的主模块:

import sharedstuff as shared

and some other modules that actually populated these arrays.以及其他一些实际填充这些数组的模块。 These are called by the main module.这些由主模块调用。 When exiting these other modules I can clearly see that the arrays are populated.退出这些其他模块时,我可以清楚地看到数组已填充。 But when reading them back in the main module, they were empty.但是当在主模块中读回它们时,它们是空的。 This was rather strange for me (well, I am new to Python).这对我来说很奇怪(好吧,我是 Python 新手)。 However, when I change the way I import the sharedstuff.py in the main module to:但是,当我将在主模块中导入 sharedstuff.py 的方式更改为:

from globals import *

it worked (the arrays were populated).它起作用了(填充了数组)。

Just sayin'只是在说'

The easiest solution to this particular problem would have been to add another function within the module that would have stored the cursor in a variable global to the module.解决这个特定问题的最简单方法是在模块中添加另一个函数,该函数将光标存储在模块的全局变量中。 Then all the other functions could use it as well.然后所有其他功能也可以使用它。

module1:模块1:

cursor = None

def setCursor(cur):
    global cursor
    cursor = cur

def method(some, args):
    global cursor
    do_stuff(cursor, some, args)

main program:主程序:

import module1

cursor = get_a_cursor()
module1.setCursor(cursor)
module1.method()

Since globals are module specific, you can add the following function to all imported modules, and then use it to:由于全局变量是特定于模块的,因此您可以将以下函数添加到所有导入的模块中,然后将其用于:

  • Add singular variables (in dictionary format) as globals for those添加奇异变量(以字典格式)作为这些变量的全局变量
  • Transfer your main module globals to it .将您的模块全局变量传输给它。

addglobals = lambda x: globals().update(x) addglobals = lambda x: globals().update(x)

Then all you need to pass on current globals is:然后,您需要传递当前全局变量的是:

import module导入模块

module.addglobals(globals()) module.addglobals(globals())

Since I haven't seen it in the answers above, I thought I would add my simple workaround, which is just to add a global_dict argument to the function requiring the calling module's globals, and then pass the dict into the function when calling;由于我在上面的答案中没有看到它,我想我会添加一个简单的解决方法,即在需要调用模块全局变量的函数中添加一个global_dict参数,然后在调用时将 dict 传递给函数; eg:例如:

# external_module
def imported_function(global_dict=None):
    print(global_dict["a"])


# calling_module
a = 12
from external_module import imported_function
imported_function(global_dict=globals())

>>> 12

The OOP way of doing this would be to make your module a class instead of a set of unbound methods.执行此操作的 OOP 方法是使您的模块成为一个类,而不是一组未绑定的方法。 Then you could use __init__ or a setter method to set the variables from the caller for use in the module methods.然后你可以使用__init__或 setter 方法来设置来自调用者的变量,以便在模块方法中使用。

Update更新

To test the theory, I created a module and put it on pypi.为了测试这个理论,我创建了一个模块并将其放在 pypi 上。 It all worked perfectly.这一切都很完美。

pip install superglobals

Short answer简短的回答

This works fine in Python 2 or 3:这在 Python 2 或 3 中运行良好:

import inspect

def superglobals():
    _globals = dict(inspect.getmembers(
                inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
    return _globals

save as superglobals.py and employ in another module thusly:保存为superglobals.py并因此在另一个模块中使用:

from superglobals import *

superglobals()['var'] = value

Extended Answer扩展答案

You can add some extra functions to make things more attractive.您可以添加一些额外的功能,使事情更有吸引力。


def superglobals():
    _globals = dict(inspect.getmembers(
                inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
    return _globals

def getglobal(key, default=None):
    """
    getglobal(key[, default]) -> value
    
    Return the value for key if key is in the global dictionary, else default.
    """
    _globals = dict(inspect.getmembers(
                inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
    return _globals.get(key, default)

def setglobal(key, value):
    _globals = superglobals()
    _globals[key] = value

def defaultglobal(key, value):
    """
    defaultglobal(key, value)

    Set the value of global variable `key` if it is not otherwise st
    """
    _globals = superglobals()
    if key not in _globals:
        _globals[key] = value

Then use thusly:然后这样使用:

from superglobals import *

setglobal('test', 123)
defaultglobal('test', 456)
assert(getglobal('test') == 123)

Justification理由

The "python purity league" answers that litter this question are perfectly correct, but in some environments (such as IDAPython) which is basically single threaded with a large globally instantiated API, it just doesn't matter as much .乱扔这个问题的“python 纯度联盟”答案是完全正确的,但是在某些基本上是单线程的环境(例如 IDAPython)中,它基本上是具有大型全局实例化 API 的单线程,它并不重要

It's still bad form and a bad practice to encourage, but sometimes it's just easier.这仍然是一种不好的形式,也是一种不好的鼓励做法,但有时它更容易。 Especially when the code you are writing isn't going to have a very long life.尤其是当您编写的代码不会有很长的寿命时。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM