簡體   English   中英

導入模塊中全局變量的可見性

[英]Visibility of global variables in imported modules

我在 Python 腳本中遇到了一些導入模塊的問題。 我會盡力描述錯誤,為什么會遇到它,以及為什么我要使用這種特殊的方法來解決我的問題(我將在稍后描述):

假設我有一個模塊,我在其中定義了一些實用函數/類,它們引用了將要導入該輔助模塊的命名空間中定義的實體(讓“a”成為這樣的實體):

模塊1:

def f():
    print a

然后我有主程序,其中定義了“a”,我想將這些實用程序導入其中:

import module1
a=3
module1.f()

執行程序會觸發如下錯誤:

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

過去(兩天前,d'uh) 也提出了類似的問題,並提出了幾種解決方案,但我認為這些並不符合我的要求。 這是我的特定背景:

我正在嘗試制作一個 Python 程序,它連接到 MySQL 數據庫服務器並使用 GUI 顯示/修改數據。 為了簡潔起見,我在一個單獨的文件中定義了一堆與 MySQL 相關的輔助/實用程序函數。 但是它們都有一個公共變量,我最初實用程序模塊中定義,它是 MySQLdb 模塊中的游標對象。 后來我意識到應該在主模塊中定義游標對象(用於與數據庫服務器通信),以便主模塊和導入到其中的任何內容都可以訪問該對象。

最終結果將是這樣的:

實用程序_module.py:

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

我的主要模塊:

程序.py:

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

然后,一旦我嘗試調用任何實用程序函數,它就會觸發上述“未定義全局名稱”錯誤。

一個特別的建議是在實用程序文件中有一個“from program import cur”語句,例如:

實用程序_module.py:

from program import cur
#rest of function definitions

程序.py:

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

但這是循環導入或類似的東西,最重要的是,它也會崩潰。 所以我的問題是:

我怎么能讓主模塊中定義的“cur”對象對導入到其中的輔助函數可見?

感謝您的寶貴時間,如果解決方案已在其他地方發布,我深表歉意。 我自己找不到答案,而且我的書中沒有更多的技巧。

Python 中的全局變量對於一個模塊來說是全局的,而不是在所有模塊中。 (很多人對此感到困惑,因為在 C 語言中,全局在所有實現文件中都是相同的,除非您明確將其static 。)

有不同的方法可以解決這個問題,具體取決於您的實際用例。


在走這條路之前,問問自己這是否真的需要全球化。 也許你真的想要一個以f作為實例方法的類,而不僅僅是一個自由函數? 然后你可以做這樣的事情:

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

如果您確實想要一個全局變量,但它只是供module1使用,請將其設置在該模塊中。

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

另一方面,如果a被很多模塊共享,請將其放在其他地方,並讓每個人都導入它:

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

…並且,在module1.py中:

import shared_stuff
def f():
    print shared_stuff.a

不要使用from導入,除非該變量是常量。 from shared_stuff import a將創建一個新a變量,該變量初始化為導入時引用的任何shared_stuff.a ,並且這個新a變量不會受到對shared_stuff.a的分配的影響。


或者,在極少數情況下,您確實需要它在任何地方都是真正的全局,例如內置,將其添加到內置模塊中。 Python 2.x 和 3.x 之間的確切細節有所不同。 在 3.x 中,它的工作方式如下:

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

作為一種解決方法,您可以考慮在外層設置環境變量,如下所示。

主要.py:

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

我的模塊.py:

import os

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

作為額外的預防措施,處理模塊內部未定義 MYVAL 的情況。

函數使用定義它的模塊的全局變量。例如,您應該設置module1.a = 3而不是設置a = 3 = 3 。 因此,如果您希望curutilities_module模塊中作為全局變量使用,請設置utilities_module.cur模塊.cur。

更好的解決方案:不要使用全局變量。 將你需要的變量傳遞給需要它的函數,或者創建一個類將所有數據捆綁在一起,並在初始化實例時傳遞。

這篇文章只是對我遇到的 Python 行為的觀察。 如果您做了與我在下面所做的相同的事情,那么您在上面閱讀的建議可能對您不起作用。

也就是說,我有一個包含全局/共享變量的模塊(如上所述):

#sharedstuff.py

globaltimes_randomnode=[]
globalist_randomnode=[]

然后我有一個導入共享內容的主模塊:

import sharedstuff as shared

以及其他一些實際填充這些數組的模塊。 這些由主模塊調用。 退出這些其他模塊時,我可以清楚地看到數組已填充。 但是當在主模塊中讀回它們時,它們是空的。 這對我來說很奇怪(好吧,我是 Python 新手)。 但是,當我將在主模塊中導入 sharedstuff.py 的方式更改為:

from globals import *

它起作用了(填充了數組)。

只是在說'

解決這個特定問題的最簡單方法是在模塊中添加另一個函數,該函數將光標存儲在模塊的全局變量中。 然后所有其他功能也可以使用它。

模塊1:

cursor = None

def setCursor(cur):
    global cursor
    cursor = cur

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

主程序:

import module1

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

由於全局變量是特定於模塊的,因此您可以將以下函數添加到所有導入的模塊中,然后將其用於:

  • 添加奇異變量(以字典格式)作為這些變量的全局變量
  • 將您的模塊全局變量傳輸給它。

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

然后,您需要傳遞當前全局變量的是:

導入模塊

module.addglobals(globals())

由於我在上面的答案中沒有看到它,我想我會添加一個簡單的解決方法,即在需要調用模塊全局變量的函數中添加一個global_dict參數,然后在調用時將 dict 傳遞給函數; 例如:

# 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

執行此操作的 OOP 方法是使您的模塊成為一個類,而不是一組未綁定的方法。 然后你可以使用__init__或 setter 方法來設置來自調用者的變量,以便在模塊方法中使用。

更新

為了測試這個理論,我創建了一個模塊並將其放在 pypi 上。 這一切都很完美。

pip install superglobals

簡短的回答

這在 Python 2 或 3 中運行良好:

import inspect

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

保存為superglobals.py並因此在另一個模塊中使用:

from superglobals import *

superglobals()['var'] = value

擴展答案

您可以添加一些額外的功能,使事情更有吸引力。


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

然后這樣使用:

from superglobals import *

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

理由

亂扔這個問題的“python 純度聯盟”答案是完全正確的,但是在某些基本上是單線程的環境(例如 IDAPython)中,它基本上是具有大型全局實例化 API 的單線程,它並不重要

這仍然是一種不好的形式,也是一種不好的鼓勵做法,但有時它更容易。 尤其是當您編寫的代碼不會有很長的壽命時。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM