简体   繁体   中英

How to you get the information that a wrapper function returns when using a decorator function in Python?

I am trying to access the time to execute a function that is inside a decorator function.

I followed the direction in this post because I was unable to get the function to take the arguments I passed to it. Now I am unsure of how to get the data from calling the function.

Here is my code:

import time
from functools import wraps

def sort_timer(func):
    def outer(func):
        @wraps(func)
        def inner(*args, **kwargs):
            start = time.perf_counter()
            func(*args, **kwargs)
            finish = time.perf_counter()
            return start - finish
        return inner
    return outer

@sort_timer
def bubble_sort(a_list):
    """Sorts a_list in ascending order"""
    for pass_num in range(len(a_list) - 1):
        for index in range(len(a_list) - 1 - pass_num):
            if a_list[index] > a_list[index + 1]:
                temp = a_list[index]
                a_list[index] = a_list[index + 1]
                a_list[index + 1] = temp

list1 = [60, 19, 22, 14, 43, 27, 3, 77]
x = bubble_sort(list1)
print(x)

It would appear that what is being returned is the inner function. Here is what is logged to the console:

<function sort_timer.<locals>.outer.<locals>.inner at 0x0000027167770310>

Any insight would be appreciated. Thank you.

There appears to be an error in the answer to which you refer. (I'll see about editing and/or commenting on it to fix it.)

It can be fixed by changing return outer to return outer(func) . As shown, it's returning the outer function itself; what you want to return is the result of calling that outer function on your original function, which produces the wrapper you actually want.

That said, in your code as shown, you're not really using the double-wrapper technique to any advantage. Unless you plan on supplying additional arguments to the decorator itself (not the decorated function), you could simplify like so:

def sort_timer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        start = time.perf_counter()
        func(*args, **kwargs)
        finish = time.perf_counter()
        return start - finish
    return inner

Why not using a simple decorator for timing like eg mentioned in timeit versus timing decorator ?

import time
import functools

def timeit(f):
    @functools.wraps(f)
    def timed(*args, **kw):

        ts = time.time()
        result = f(*args, **kw)
        te = time.time()

        print(f"func:{f.__name__} args:{args}{'' if len(kw) == 0 else kw} took: {(te-ts)*1000 :.3f} msec")
        return result

    return timed

@timeit
def bubble_sort(a_list):
    """Sorts a_list in ascending order"""
    for pass_num in range(len(a_list) - 1):
        for index in range(len(a_list) - 1 - pass_num):
            if a_list[index] > a_list[index + 1]:
                temp = a_list[index]
                a_list[index] = a_list[index + 1]
                a_list[index + 1] = temp

list1 = [60, 19, 22, 14, 43, 27, 3, 77]
x = bubble_sort(list1)

which prints:

func:bubble_sort args:([3, 14, 19, 22, 27, 43, 60, 77],) took: 0.029 msec

The @functools.wraps(f) takes f 's meta information and copies it to the decorating function timed , so that the resulting decorated function keeps the features of the original/target function.

Here is the code for calculating execute time of a function:

import time
 
def timer(func):
  def wrapper(*args, **kwargs):
    start = time.time()
    func(*args, **kwargs)
    end = time.time() - start
    print(end)
  return wrapper

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM