从函数声明中收集参数名称

Gather the param names from your function declaration

我有以下通用调试器:

def debugger(method):

    def dec(*args, **kwargs):
        print("\ndebugging '{name}':\n".format(name=method.__name__))
        FOUR_SPACES = " " * 4
        EIGHT_SPACES = " " * 8
        if args:
            print("{four}args:\n".format(four=FOUR_SPACES))
            for arg in args:
                print("%s%s" % (EIGHT_SPACES, arg))
        if kwargs:
            for key, value in kwargs.items():
                print("{four}{key}:\n{eight}{value}\n".format(
                    four=FOUR_SPACES,
                    key=key,
                    eight=EIGHT_SPACES,
                    value=value
                ))

        if args or kwargs:
            print("\n")

        result = method(*args, **kwargs)
        return result

    return dec

@debugger
def generate_minutes_in_timespan(start, end, cutoff_date=None):
    # cutoff_date is just example of kwarg
    minutes_delta = (end - start).seconds / 60
    datetimes = []
    delta_range = range(0, minutes_delta + 1)
    return [start + timedelta(minutes=i) for i in delta_range]

调试器会做:

debugging 'generate_minutes_in_timespan':

    args:

        2017-08-31 17:19:00
        2017-09-01 12:05:00

这让我有点高兴,但请注意我错过了真实信息。

我想要的是:

debugging 'generate_minutes_in_timespan':

    args:

        start = 2017-08-31 17:19:00
        end = 2017-09-01 12:05:00

    *args:

        not allowed

    kwargs:

        cutoff_date = None

    *kwargs:

        not allowed

这些参数具有语义,可以用作 startend...它们不是 *args

有没有深入python标准库的方法我可以阅读这个方法generate_minutes_in_timespan并提取声明、所需位置参数的名称和顺序,以及任何声明的kwargs的名称(就像声明 cutoff_date=None,我想知道 cutoff_date 是作为 kwarg 提供的,而不是可选的 **kwargs...)

我争取 {'args': [('start', 'the value given'), ('end', 'the user given value')], 'star_args': None, 'kwargs': {'cutoff_date': None}, 'star_kwargs': None} 以获得完整的调试信息。

在装饰器中,使用 inspect 模块发现 arg 名称:

>>> def generate_minutes_in_timespan(start, end, cutoff_date=None):
...     ...
... 
>>> inspect.getargspec(generate_minutes_in_timespan)
ArgSpec(args=['start', 'end', 'cutoff_date'], varargs=None, keywords=None, defaults=(None,))
               ^        ^
               |________|_____ here they are...

将传递的参数与实际参数名称进行匹配留作 OP 的练习。

使用inspect.signature(method).bind:

for name, val in inspect.signature(method).bind(*args, **kwargs).arguments:
    print('{} = {}'.format(name, val))

如果您使用的是 Python 2,您可以使用 PyPI 上的 backport 此功能。

结果不错:

import inspect

def debugger(method):

    def dec(*args, **kwargs):
        print("\ndebugging '{name}':\n".format(name=method.__name__))
        FOUR_SPACES = " " * 4
        EIGHT_SPACES = " " * 8

        inspector = inspect.getargspec(method)

        arg_names = inspector.args
        var_args = inspector.varargs
        keyword_args = inspector.keywords

        def wrap_string_in_quotes(value):
            if isinstance(value, str):
                return '"' + value + '"'
            else:
                return value

        if args:
            print("{four}args:\n".format(four=FOUR_SPACES))
            for index, arg_name in enumerate(arg_names):
                print(
                    "{eight}{name} = {arg}" \
                    .format(
                        eight=EIGHT_SPACES,
                        name=arg_name,
                        arg=wrap_string_in_quotes(args[index])
                    )
                )

        if var_args:
            start = len(arg_names)
            these_args = args[start:]
            print("\n{four}*{star_arg_name}:\n".format(four=FOUR_SPACES, star_arg_name=var_args))
            for this_var_arg in these_args:
                print("{eight}{arg}".format(eight=EIGHT_SPACES, arg=wrap_string_in_quotes(this_var_arg)))

        if kwargs:
            print("\n{four}**{kwargs_name}:\n".format(four=FOUR_SPACES, kwargs_name=keyword_args))
            for key, value in kwargs.items():
                print("{eight}{key} = {value}\n".format(
                    eight=EIGHT_SPACES,
                    key=key,
                    value=wrap_string_in_quotes(value)
                ))

        if args or kwargs:
            print("\n")

        result = method(*args, **kwargs)
        return result

    return dec


@debugger
def one_two_kwarg_three_star_args_star_kwargs(one, two, three=None,  *args, **kwargs):
    pass

one_two_kwarg_three_star_args_star_kwargs(1, 2, 3, 'star arg 1', 'star arg 2', blah='blah, kwarg')

$ python debugger.py 

debugging 'one_two_kwarg_three_star_args_star_kwargs':

    args:

        one = 1
        two = 2
        three = 3

    *args:

        "star arg 1"
        "star arg 2"

    **kwargs:

        blah = "blah, kwarg"