在 Python 中取消导入函数的别名



在显示调用函数的参数和值的实用程序函数中,我需要知道从另一个模块导入的可能别名函数的原始名称。这可能适用于导入时混叠时的简单情况吗?

下面是一个简化的用例,我首先介绍 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

一些相关问题,这些问题不能解决这种别名的逆转:

  • Python 中函数的别名
  • 在 Python 中查找动态方法的名称
  • 获取在 Python 中调用函数模块__name__
  • 打印 Python 函数参数的名称和值

事实证明,找出为debug_printformat_args定义了哪个别名是相当困难的,但幸运的是,我确实有代码上下文,并且可以执行反向操作,定位我的代码上下文的哪一部分实际上是我的函数之一。

导致此解决方案的以下思路部分受到Martijn Pieters与抽象语法树相关的评论的启发,部分受到SuperBiasedMan给出的与做help(fargs)有关的提示:

  • help(fargs)实际上列出了format_args函数
  • 在IPython中,使用help??,我发现了它使用pydoc.help
  • pydoc.py 的源代码在这里找到
  • 找到呼叫序列:帮助>文档>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()。如果您发现此设计中的改进或缺陷,请发表评论(或回答(。

相关内容

  • 没有找到相关文章

最新更新