2011-03-25 34 views
9

我想收集有關崩潰的信息,我無法弄清楚如何獲取正在崩潰的函數中使用的全局變量。在Python中,如何獲取函數中使用的全局變量?

import inspect 

fun = 222 
other = "junk" 

def test(): 
    global fun 
    harold = 888 + fun 
    try: 
     harold/0 
    except: 
     frames = inspect.trace() 
     print "Local variables:" 
     print frames[0][0].f_locals 

     print "All global variables, not what I want!" 
     print frames[0][0].f_globals 

test() 

test()只使用「fun」,但f_globals給出所有可用的全局變量。有什麼方法可以獲得這個函數正在使用的全局變量嗎?

+0

對於「使用」的大多數定義,它也使用'inspect'。如果你的意思是「使用」的其他內容,請具體說明。 – delnan 2011-03-25 17:02:55

+0

目的是爲了挑選出全局中所有額外的東西。有一個通過*導入的核心庫,所以有太多的全局變量來報告它們。更新了實際使用'fun'的問題。 – 2011-03-25 18:25:51

回答

1

骯髒的方法是使用inspect.getsourcelines()並搜索包含global <varname>的行。有沒有好的方法,至少不在inspect模塊。

0

正如您已經發現的那樣,屬性f_globals爲您提供了定義函數的全局命名空間。

從我所看到的,找出哪些全局變量實際使用的唯一方法是用dis反彙編函數的字節碼;尋找的字節碼STORE_NAMESTORE_GLOBALDELETE_GLOBAL

3

檢查了這一點

a = 10 

def test(): 
    global a 
    a = 12 
    b = 12 

print "co_argcount = ",test.__code__.co_argcount 
print "co_cellvars = ",test.__code__.co_cellvars 
print "co_code = ",test.__code__.co_code 
print "co_consts = ",test.__code__.co_consts 
print "co_filename = ",test.__code__.co_filename 
print "co_firstlineno = ",test.__code__.co_firstlineno 
print "co_flags = ",test.__code__.co_flags 
print "co_freevars = ",test.__code__.co_freevars 
print "co_lnotab = ",test.__code__.co_lnotab 
print "co_name = ",test.__code__.co_name 
print "co_names = ",test.__code__.co_names 
print "co_nlocals = ",test.__code__.co_nlocals 
print "co_stacksize = ",test.__code__.co_stacksize 
print "co_varnames = ",test.__code__.co_varnames 
+0

你可能想提到這需要python 2.6或更高版本。 – 2011-03-25 17:18:00

+1

謝謝!幸運的是,我正在使用2.6,所以'test .__ code __。co_names'正是我所需要的。在這個特定的情況下,我正在裝飾功能來處理它們,如果它們失敗了,所以我有手頭的功能。 – 2011-03-25 18:42:00

1

我需要還自己。這是我的解決方案。非快速路徑覆蓋大多數情況下,你都可能感興趣。

def iterGlobalsUsedInFunc(f, fast=False, loadsOnly=True): 
    if hasattr(f, "func_code"): code = f.func_code 
    else: code = f 
    if fast: 
     # co_names is the list of all names which are used. 
     # These are mostly the globals. These are also attrib names, so these are more... 
     for name in code.co_names: 
      yield name 
    else: 
     # Use the disassembly. Note that this will still not 
     # find dynamic lookups to `globals()` 
     # (which is anyway not possible to detect always). 
     import dis 
     ops = ["LOAD_GLOBAL"] 
     if not loadsOnly: 
      ops += ["STORE_GLOBAL", "DELETE_GLOBAL"] 
     ops = map(dis.opmap.__getitem__, ops) 
     i = 0 
     while i < len(code.co_code): 
      op = ord(code.co_code[i]) 
      i += 1 
      if op >= dis.HAVE_ARGUMENT: 
       oparg = ord(code.co_code[i]) + ord(code.co_code[i+1])*256 
       i += 2 
      else: 
       oparg = None 
      if op in ops: 
       name = code.co_names[oparg] 
       yield name 

    # iterate through sub code objects 
    import types 
    for subcode in code.co_consts: 
     if isinstance(subcode, types.CodeType): 
      for g in iterGlobalsUsedInFunc(subcode, fast=fast, loadsOnly=loadsOnly): 
       yield g 

一個更新的版本可能here


我的使用情況:

我有一些模塊(songdb),其中有一些全局數據庫對象,我想懶洋洋地加載它們一次我叫它使用全局數據庫變量的函數。我可以用懶惰的加載程序手動修飾這些函數,或者我可以通過我的iterGlobalsUsedInFunc函數自動檢測哪些函數需要它。

這基本上是代碼(full code;實際上延長了類現在),其中init自動裝飾這樣的功能:

DBs = { 
    "songDb": "songs.db", 
    "songHashDb": "songHashs.db", 
    "songSearchIndexDb": "songSearchIndex.db", 
    } 
for db in DBs.keys(): globals()[db] = None 

def usedDbsInFunc(f): 
    dbs = [] 
    for name in utils.iterGlobalsUsedInFunc(f, loadsOnly=True): 
     if name in DBs: 
      dbs += [name] 
    return dbs 

def init(): 
    import types 
    for fname in globals().keys(): 
     f = globals()[fname] 
     if not isinstance(f, types.FunctionType): continue 
     dbs = usedDbsInFunc(f) 
     if not dbs: continue 
     globals()[fname] = lazyInitDb(*dbs)(f) 

def initDb(db): 
    if not globals()[db]: 
     globals()[db] = DB(DBs[db]) 

def lazyInitDb(*dbs): 
    def decorator(f): 
     def decorated(*args, **kwargs): 
      for db in dbs: 
       initDb(db) 
      return f(*args, **kwargs) 
     return decorated 
    return decorator 

另一種解決方案將是使用哪個懶散地加載數據庫中的對象的代理。我在這個項目的其他地方使用過,因此我也實現了這樣的對象代理;如果你有興趣,請看這裏:utils.pyObjectProxy

相關問題