首页 > 解决方案 > 如何知道传递给函数的值是什么?

问题描述

def warn_slow(func):
    def wrapper(*args, **kwargs):
        start_time=time.time()
        func(*args,**kwargs)
        time_elapsed=time.time()-start_time
        if time_elapsed>2:
            return (f"execution of {func.__name__} with {func.__code__.co_names} arguments took more than 2 seconds")
    return wrapper

我想编写一个装饰器来计算函数执行的时间,如果超过 2 秒,将返回文本“使用 {function variables} 参数执行 {func.__name__} 耗时超过 2 秒”。我们如何知道哪些值作为参数传递?我试过func.__code__.co_names了,但是它只返回参数名称。

标签: python

解决方案


您可以通过合并@warvariuc 对打印函数调用详细信息(参数名称和有效值)的装饰器问题的回答来做类似的事情?因此进入你自己的装饰器:

import inspect
import time

def warn_slow(func):
    '''Decorator that warns when a call to the function takes too long.'''
    TIME_LIMIT = 2

    def wrapper(*args, **kwargs):
        func_args = inspect.signature(func).bind(*args, **kwargs).arguments
        func_args_str = ", ".join("{}={!r}".format(*item) for item in func_args.items())
        start_time = time.time()
        result = func(*args, **kwargs)
        time_elapsed = time.time() - start_time
        if time_elapsed > TIME_LIMIT:
            print(f"execution time of {func.__module__}.{func.__qualname__}"
                  f"({func_args_str}) took more than {TIME_LIMIT} seconds")
        return result

    return wrapper


@warn_slow
def test_func_a(a, b, c=None):
    time.sleep(1)

@warn_slow
def test_func_b(a, b, c=None):
    time.sleep(2.5)

test_func_a(1, 2, c=13)
test_func_b(3, 4, c=42)

产生的输出:

execution time of __main__.test_func_b(a=3, b=4, c=42) took more than 2 seconds

概括

编程时,通常最好避免在代码中“硬编码”常量字面值。在这种情况下,装饰器可以通过允许覆盖 2 秒时间限制来变得更加灵活。一种方法需要让它接受一个可选参数,指定限制应该是什么。为了实现这一点,我使用了@Nicole回答中的通用“装饰器装饰器”来回答关于如何实现让它们这样做的问题。我选择它的部分原因是它需要对上面显示的装饰器进行最少的修改(并且它本身非常通用)。

结果如下:

import inspect
import time


def optional_arg_decorator(fn):  # From https://stackoverflow.com/a/20966822/355230
    def wrapped_decorator(*args):
        if len(args) == 1 and callable(args[0]):
            return fn(args[0])
        else:
            def real_decorator(decoratee):
                return fn(decoratee, *args)
            return real_decorator
    return wrapped_decorator


@optional_arg_decorator
def warn_slow(func, time_limit=2):
    ''' Decorator that warns when a call to the function takes too long.
            Accepts optional argument to override default time limit.
    '''
    def wrapper(*args, **kwargs):
        func_args = inspect.signature(func).bind(*args, **kwargs).arguments
        func_args_str = ", ".join("{}={!r}".format(*item) for item in func_args.items())
        start_time = time.time()
        result = func(*args, **kwargs)
        time_elapsed = time.time() - start_time
        if time_elapsed > time_limit:
            print(f"execution time of {func.__module__}.{func.__qualname__}"
                  f"({func_args_str}) took more than {time_limit} seconds")
        return result
    return wrapper


@warn_slow
def test_func_a(a, b, c=None):
    time.sleep(1)

@warn_slow
def test_func_b(a, b, c=None):
    time.sleep(2.5)

@warn_slow(3)  # Override default time limit.
def test_func_c(a, b, c=None):
    time.sleep(3.1)

test_func_a(1, 2, c=10)
test_func_b(3, 4, c=2)
test_func_c(5, 6, c=42)

输出:

execution time of __main__.test_func_b(a=3, b=4, c=2) took more than 2 seconds
execution time of __main__.test_func_c(a=5, b=6, c=42) took more than 3 seconds

推荐阅读