在显示调用函数的参数和值的实用程序函数中,我需要知道从另一个模块导入的可能别名函数的原始名称。这可能适用于导入时混叠时的简单情况吗?
下面是一个简化的用例,我首先介绍 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_print
或format_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()
。如果您发现此设计中的改进或缺陷,请发表评论(或回答(。