【问题标题】:Unalias an imported function in Python?Unalias Python中的导入函数?
【发布时间】:2016-02-05 14:49:44
【问题描述】:

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

这是一个简化的用例,我首先展示来自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 p>

一些相关的问题,没有解决了这种别名的逆转:

【问题讨论】:

  • 简短回答:不,没有,不是没有对调用框架的源代码进行广泛的 AST 解析和分析,因此您可以猜测用于产生调用的名称。
  • @MartijnPieters,AST?那是抽象语法树吗?
  • 是的,您必须加载源代码,然后分析调用是如何进行的以及可调用对象的名称。请注意,您可以创建其他不一定有名称的引用; callables = [fargs, debug], then callables[0]()` 使用对列表中函数对象的引用。
  • @MartijnPieters,正如开头所述,我的目标是简单的别名,所以如果有人通过使用像你的 callables 这样的东西破坏了这个功能,那么他们就靠自己了! :-)
  • @MartijnPieters,由于我有部分代码上下文,我可以尝试其中的单词并检查它们是否评估为我的两种方法之一吗? (在某些字典中使用查找...)

标签: python python-2.7 reflection


【解决方案1】:

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

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

  • help(fargs) 实际上列出了format_args 函数
  • 在 IPython 中,使用help??,我发现它使用pydoc.help 的提示
  • 找到了pydoc.py的源代码here
  • 找到调用顺序:help > doc > render_doc > resolve > 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()。如果您发现此设计有改进或缺陷,请发表评论(或回答)。

【讨论】:

    猜你喜欢
    • 2011-10-12
    • 1970-01-01
    • 1970-01-01
    • 2015-03-05
    • 1970-01-01
    • 1970-01-01
    • 2018-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多