2015-11-04 39 views
1

在顯示調用函數的參數和值的實用函數中,我需要知道從另一個模塊導入的可能的別名函數的原始名稱。這可能適用於導入時使用別名的簡單情況嗎?Unalias在Python中導入的函數?

下面是一個簡化的用例,其中,I首先從utilities.py模塊呈現一些代碼:

import inspect 

DEBUG_FLAG = True 

def _log_args(*args): 
    """Uses reflection to returning passing argument code with values.""" 

    prev_frame = inspect.currentframe().f_back 
    func_name = prev_frame.f_code.co_name 
    code_context = inspect.getframeinfo(prev_frame.f_back).code_context[0].strip() 

    # Do some magic, which does work _unless_ func_name is aliased :-) 
    print('code context: {}'.format(code_context)) 
    print('func_name : {}'.format(func_name)) 
    return ', '.join(str(arg) for arg in args) 

def format_args(*args): 
    """Returns string with name of arguments with values.""" 
    return _log_args(args) 

def debug_print(*args): 
    """Prints name of arguments with values.""" 
    if DEBUG_FLAG: 
     print _log_args(args) 

這裏是一些代碼訪問這些功能首先通過原始名稱,然後通過別名:

from utilities import debug_print, format_args, debug_print as debug, format_args as fargs 

def main(): 
    a, b = "text", (12, 13) 

    print "== Unaliased ==" 
    test_text = format_args(a, b) 
    print test_text # Returns 
    debug_print(a, b) 

    print "\n== Aliased ==" 
    test_text = fargs(a, b) 
    print test_text 
    debug(a, b) 

if __name__ == '__main__': 
    main() 

從這個輸出是:

== Unaliased == 
code context: test_text = format_args(a, b) 
func_name : format_args 
('text', (12, 13)) 
code context: debug_print(a, b) 
func_name : debug_print 
('text', (12, 13)) 

== Aliased == 
code context: test_text = fargs(a, b) 
func_name : format_args 
('text', (12, 13)) 
code context: debug(a, b) 
func_name : debug_print 
('text', (12, 13)) 

如可被發現我已經找到了正確的代碼上下文,並且我找到了調用函數的名字,但是第一次報告的是別名,而後者報告了實際的名稱。所以我的問題是是否可以反轉操作,以便我可以知道format_args已被別名爲fargs,並且debug_print已被別名爲debug

一些相關的問題,這些問題做地址這種逆轉走樣:

+3

簡而言之:沒有,沒有,沒有廣泛的AST解析和調用幀的源代碼分析,所以你可以猜測用什麼名字來產生調用。 –

+1

@MartijnPieters,AST?那是抽象語法樹嗎? – holroy

+2

是的,您必須加載源代碼,然後分析調用的方式以及可調用對象的名稱。請注意,您可以創建其他不一定具有名稱的引用; 'callables = [fargs,debug],然後'callables [0]()'使用對列表中函數對象的引用。 –

回答

0

事實證明,找出爲debug_printformat_args定義了哪個別名是相當困難的,但幸運的是,我確實有代碼上下文,並且可以執行相反的操作來查找我的代碼上下文的哪一部分實際上是我的函數。

的想法導致了該解決方案的後續列車的部分原因是由Martijn Pieters與抽象語法樹提出的意見的啓發,部分由與做help(fargs)通過SuperBiasedMan給出一個提示:

  • help(fargs)實際列出的功能format_args
  • 在IPython中,使用help??,我發現,它使用pydoc.help
  • 是pydoc的源代碼的提示。PY發現here
  • 找到調用序列:幫助>文檔>render_doc>決心>name = getattr(thing, '__name__', None)
  • 試圖在我的測試代碼getattr(fargs, '__name__', None),和它的工作
  • 試過getattr('fargs', ...),並且失敗
  • 經過一番搜索發現globals()['fargs']的確返回了功能對象
  • 從我code_context提取標記,並寫一些代碼來執行各種查詢

這一切都導致了以下工作代碼:

def _log_args(*args): 
    """Uses reflection to returning passing argument code with values.""" 

    prev_frame = inspect.currentframe().f_back 
    func_name = prev_frame.f_code.co_name 
    code_context = inspect.getframeinfo(prev_frame.f_back).code_context[0].strip() 

    # Do some magic, which does work _unless_ func_name is aliased :-) 
    print('code context  : {}'.format(code_context)) 
    print('func_name  : {}'.format(func_name)) 

    # Get globals from the calling frame 
    globals_copy = prev_frame.f_back.f_globals 

    tokens = re.compile('[_a-zA-Z][a-zA-Z_0-9]*').findall(code_context) 
    for token in tokens: 
     print(' Checking token : {}'.format(token)) 

     # Check if token is found as an object in globals()   
     code_object = globals_copy.get(token, None) 
     if not code_object: 
      continue 

     # Check if code_object is one of my userdefined functions 
     if inspect.isfunction(code_object): 
      code_func_name = getattr(code_object, '__name__', None) 
     else: 
      continue 

     # Check if expanded token is actually an alias (or equal) to func_name 
     if code_func_name == func_name: 
      func_name = token 
      break 
    else: 
     # For-loop went through all tokens, and didn't find anything 
     func_name = None 

    if func_name: 
     print('Calling function : {}'.format(func_name)) 
    else: 
     print('Didn\'t find a calling function?!') 

    return ', '.join(str(arg) for arg in args) 

我不知道,這取決於調用函數之中出現在代碼上下文中,如果你將代碼分成幾行,它會破壞這種方法。另一個警告是如果有人通過列表或字典調用函數。但是,這主要是爲了調試目的,並且可以記錄下來,他們不應該這樣做。

輸出現在是:現在

== Unaliased == 
code context  : test_text = format_args(a, b) 
func_name  : format_args 
Calling function : format_args 
('text', (12, 13)) 
code context  : debug_print(a, b) 
func_name  : debug_print 
Calling function : debug_print 
('text', (12, 13)) 

== Aliased == 
code context  : test_text = fargs(a, b) 
func_name  : format_args 
Calling function : fargs 
('text', (12, 13)) 
code context  : debug(a, b) 
func_name  : debug_print 
Calling function : debug 
('text', (12, 13) 

,該輸出纔會啓用在我的追求着手製作一個不錯debug_print()。請評論(或回答),如果您在本設計中看到改進或缺陷。