TemporalParadox
TemporalParadox

Reputation: 23

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

Upvotes: 2

Views: 1773

Answers (3)

Hikash
Hikash

Reputation: 429

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)

Upvotes: 5

zvone
zvone

Reputation: 19352

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.

Upvotes: 4

Thierry Lathuille
Thierry Lathuille

Reputation: 24233

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

Upvotes: 0

Related Questions