打印Python函数参数的名称和值

时间:2015-10-13 22:33:19

标签: python python-2.7 reflection

我想创建一个debug_print()来输出调用者变量和值,稍后我会将其扩展为仅部分打印列表和dicts等等。本文仅关注打印调用者变量和值的第一部分。

这篇文章包含以下部分:

  • debug_print
  • 的当前版本
  • 构建的测试用例
  • 测试用例输出
  • 我想要的输出和问题区域
  • 有些相关问题的清单

对不起,对于一篇有点冗长的帖子,但我只是想表明我做了一些研究,并且真的希望得到一些帮助以获得一些帮助来解决我的问题(如上一节中所列) )。

debug_print

的当前版本
import inspect

def debug_print(*args):

    try:  # find code_context
        # First try to use currentframe() (maybe not available in all implementations)
        frame = inspect.currentframe()
        if frame:
            # Found a frame, so get the info, and strip space from the code_context
            code_context = inspect.getframeinfo(frame.f_back).code_context[0].strip()
        else:

            # No frame, so use stack one level above us, and strip space around
            # the 4th element, code_context
            code_context = inspect.stack()[1][4][0].strip()

    finally:
         # Deterministic free references to the frame, to be on the safe side
         del frame

    print('Code context : {}'.format(code_context))
    print('Value of args: {}\n'.format(args))

构建的测试用例

# Test with plain variables
a = 0.2
b = 1.2
c = a + 1
debug_print(a, b, c, b+2)

# Test with list, list of lists, tuples and dict's
debug_print([4.1, 4.2], [[4.00, 4.01], ["4.1.0", '4.1.1']])
debug_print((5.1,   5.2), {6.1: 6.2})

# Test with func's or func aliases
def my_func():
   return 7
my_alias_func = my_func

debug_print(my_func, my_alias_func, my_alias_func())


# Test with nested func's and list slices
my_list = [1, 2, 3, 4, 5]

def first_level():
    def second_level():
         debug_print(my_list[:2], my_list[3:])

    second_level()

# Execute
first_level()

# Test with multi-line call
debug_print(a, b,
            'multi-line call', c)

测试用例的输出

Code context : debug_print(a, b, c, b+2)
Value of args: (0.2, 1.2, 1.2, 3.2)

Code context : debug_print([4.1, 4.2], [[4.00, 4.01], ["4.1.0", '4.1.1']])
Value of args: ([4.1, 4.2], [[4.0, 4.01], ['4.1.0', '4.1.1']])

Code context : debug_print((5.1,   5.2), {6.1: 6.2})
Value of args: ((5.1, 5.2), {6.1: 6.2})

Code context : debug_print(my_func, my_alias_func, my_alias_func())
Value of args: (<function my_func at 0x110393668>, <function my_func at 0x110393668>, 7)

Code context : debug_print(my_list[:2], my_list[3:])
Value of args: ([1, 2], [4, 5])

Code context : 'multi-line call', c)
Value of args: (0.2, 1.2, 'multi-line call', 1.2)

想要输出和问题区域

我希望输出以下内容:

a: 0.2;  b: 1.2;  c: 1.2; 3.2
<list>: [4.1, 4.2];  <list of lists>: [[4.0, 4.01], ['4.1.0', '4.1.1']]
<tuple>: (5.1, 5.2);  <dict>: {6.1: 6.2}
func: my_func;  func: my_alias_func -> my_func;  my_func(): 7
my_list[:2]: [1, 2];  my_list[3:]: [4, 5]
然而,我确实从相关问题中看到,这可能会设置一个高点。但是我越接近,它就越好。

理想情况下,我可以遍历一些原始参数代码的dict 作为关键,但我也最满意的方法是从code_context中提取真正的参数代码,然后将它们与实际的args相结合。

但是我不能简单地在,上拆分代码上下文,因为它也可能是列表,字典或元组的一部分。如果可以使用某些python解析器来拆分代码上下文,那么这可能是一种可供探索的替代路径。但我想避免使用eval

到目前为止,我的搜索还没有透露任何可以获得带有代码和值的函数实际参数列表的地方。 (已经看过对f_globalsfunc_globals的引用,但它们列出了模块可用的所有内容,我发现没有办法将它们重新连接到变量arguemnt列表。)

旁注:我知道可以使用debug_print(**kwargs)之上的变体,然后使用debug_print(a=a, b=b; c=c; sum=b+2)这样的语句,但最好避免重复编写调试语句。

有些相关问题的清单

过去一直存在与类似问题相关的问题,但是虽然大多数问题涉及固定参数,或者只是显示名称,但我想显示名称和值,如果可用的话。或者是解析代码上下文以匹配给定参数的指南。但这里有一些相关的问题:

1 个答案:

答案 0 :(得分:0)

我不确定任何语法可能比你已经获得的语法更好,但是:

def pprint(arg):
    of = None
    if not isinstance(arg, (str, bytes)):
        try:
            of = type(arg[0]).__name__
        except (IndexError, TypeError, KeyError):
            of = None
    if of is None:
        if hasattr(arg, '__name__'):
            return '{}({})'.format(type(arg).__name__, arg.__name__)
        else:
            return '{}({})'.format(type(arg).__name__, repr(arg))
    else:
        return '{}<{}>{}'.format(type(arg).__name__, of, repr(arg))

def debug_print(*args):

    try:  # find code_context
            # First try to use currentframe() (maybe not available in all implementations)
        frame = inspect.currentframe()
        if frame:
            # Found a frame, so get the info, and strip space from the code_context
            code_context = inspect.getframeinfo(frame.f_back).code_context[0].strip()
        else:
            # No frame, so use stack one level above us, and strip space around
            # the 4th element, code_context
            code_context = inspect.stack()[1][4][0].strip()
    finally:
             # Deterministic free references to the frame, to be on the safe side
        del frame

    print('Code context : {}'.format(code_context))
    print('Value of args: {}\n'.format('; '.join(pprint(arg) for arg in args)))

给出:

Code context : debug_print(a, b, c,  b + 2)
Value of args: float(0.2); float(1.2); float(1.2); float(3.2)

Code context : debug_print([4.1, 4.2], [[4.00, 4.01], ["4.1.0", '4.1.1']])
Value of args: list<float>[4.1, 4.2]; list<list>[[4.0, 4.01], ['4.1.0', '4.1.1']]

Code context : debug_print((5.1,   5.2), {6.1: 6.2})
Value of args: tuple<float>(5.1, 5.2); dict({6.1: 6.2})

Code context : debug_print(my_func, my_alias_func, my_alias_func())
Value of args: function(my_func); function(my_func); int(7)

Code context : debug_print(my_list[:2], my_list[3:])
Value of args: list<int>[1, 2]; list<int>[4, 5]

Code context : 'multi-line call', c)
Value of args: float(0.2); float(1.2); str('multi-line call'); float(1.2)

尽管如此,我更喜欢repr内置pprint功能:

  • pprint([1, 'a'])

list<int>(1, 'a')这显然是错误的,我们无法做得更好,你真的想要list<int or string>吗?

repr给出[1, 'a'],这是可读和正确的。

  • pprint(1.3)

给出<float>(1.3),就像我看不到1.3是浮动的? repr给出1.3,这显然足够了。

  • ...