2015-11-04 71 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()。请评论(或回答),如果您在本设计中看到改进或缺陷。