简体   繁体   中英

Decorated function returns “None”

I'm extremely new to python, and i just encountered decorators. I'm still kinda confused by them but i am learning

i was trying to make a decorator that tells me how much time my function took to finish, but apparently when i try to use it on a function that should return something, it just returns "None"

I've seen only a couple of questions talking about this problem but none of them actually helped

Here's my code

import time


def time_it(func):  # Here i make a simple decorator function that should time my decorated function
    def wrapper(*args, **kwargs):
        t1 = time.time()
        func(*args)
        t2 = time.time()
        total = t2 - t1
        print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")

    return wrapper


@time_it
def square(nums):  # I make a function that squares every number in a list
    new_list = []
    for n in nums:
        new_list.append(n ** 2)
    return new_list


lis = [f for f in range(200000)]  # i make a list with a range of 200000
print(square(lis))  

sorry for any grammatical errors, i'm not a native english speaker

The decorator replaces square with wrapper and wrapper does not return anything. It should return the value returned by the wrapped function.

This is the correct way to do it:

def time_it(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        try:
            return func(*args, **kwargs)
        finally:
            t2 = time.time()
            total = t2 - t1
            print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")

    return wrapper

I changed 3 things:

  • added return , so that the value is returned from decorated function
  • added **kwargs to func calls, because it may be needed if used differently
  • added try / finally block, so that the printout happens even in case of an exception, plus this makes it easier to return the value.

The problem is that your inner function return value isn't being returned. The change is noted below:

from functools import wraps

def time_it(func):  # Here i make a simple decorator function that should time my decorated function
    @wraps(func)
    def wrapper(*args, **kwargs):
        t1 = time.time()
        ## Note the change on this line -- I now store the return result from the called function 
        result = func(*args, **kwargs)
        t2 = time.time()
        total = t2 - t1
        print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")

        ## And then explicitly return the result
        return result

    return wrapper

For the decorator, you need to remember that it's just a closure, with some fancy syntax. You still need to deal with the function return parameters yourself.

A couple of additions:

  • from functools import wraps and @wraps(func)

Your decorated function doesn't return anything explicitely - so, by default, it returns None .

You can capture the output before printing the time, and return at the end:

def time_it(func):  # Here i make a simple decorator function that should time my decorated function
    def wrapper(*args, **kwargs):
        t1 = time.time()
        out = func(*args)
        t2 = time.time()
        total = t2 - t1
        print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")
        return out
    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