Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
146 views
in Technique[技术] by (71.8m points)

python - How to know what values were passed to function?

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

I want to write a decorator that will calculate the time of function execution and will return a text "execution of {func.__name__} with {function variables} arguments took more than 2 seconds" if it is longer than 2 seconds. How can we know what values were passed as arguments? I tried func.__code__.co_names, however it returns the argument names only.

question from:https://stackoverflow.com/questions/65939335/how-to-know-what-values-were-passed-to-function

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You could do something like this by incorporating @warvariuc's answer to the question Decorator that prints function call details (parameters names and effective values)? into your own decorator thusly:

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)

Output produced:

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

Generalizing

When programming, it's usual best to avoid "hard coding" constant literal values into your code as much as possible. In this case the decorator can be make more flexible by allowing the 2 second time limit to be overridden. One way of doing that requires making it accept an optional argument specifying what the limit should be. To implement that I used the generic "decorator decorator" from @Nicole's answer to a question on how to implement making them do that. I chose it partly because it required the least amount of modifications to decorator shown above (and is itself very generic).

Here's the result:

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)

Output:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.9k users

...