Оформленная функция возвращает «None»

#python #python-3.x #decorator

#python #python-3.x #декоратор

Вопрос:

Я новичок в Python, и я только что столкнулся с декораторами. Я все еще немного смущен ими, но я учусь

я пытался создать декоратор, который сообщал бы мне, сколько времени потребовалось для завершения моей функции, но, видимо, когда я пытаюсь использовать его для функции, которая должна что-то возвращать, она просто возвращает «None»

Я видел только пару вопросов, в которых говорилось об этой проблеме, но ни один из них на самом деле не помог

Вот мой код

 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))  
  

извините за любые грамматические ошибки, я не являюсь носителем английского языка

Комментарии:

1. Да, потому что wrapper всегда возвращает None .

Ответ №1:

Проблема в том, что возвращаемое значение вашей внутренней функции не возвращается. Изменение отмечено ниже:

 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
  

Для декоратора вам нужно помнить, что это просто замыкание с некоторым причудливым синтаксисом. Вам все равно нужно разобраться с параметрами возврата функции самостоятельно.

Пара дополнений:

  • from functools import wraps и @wraps(func)
    • это создаст обертку внутренней функции с некоторыми деталями, которые существуют в функции обертывания. В документации Python здесь есть небольшой пример:https://docs.python.org/3/library/functools.html

Комментарии:

1. Вероятно, также лучше, чтобы OP использовал все аргументы, например: result = func(*args, **kwargs) на всякий случай… Также не помешало бы использовать functools.wraps и здесь…

2. Да, вы правы. Я обновил пример, чтобы отразить это. Спасибо!

Ответ №2:

Декоратор заменяет square на wrapper и wrapper ничего не возвращает. Она должна возвращать значение, возвращаемое обернутой функцией.

Это правильный способ сделать это:

 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
  

Я изменил 3 вещи:

  • добавлено return , чтобы значение возвращалось из оформленной функции
  • добавлено **kwargs в func вызовы, потому что это может потребоваться при использовании по-другому
  • добавлен try / finally блок, так что распечатка происходит даже в случае исключения, плюс это упрощает возврат значения.

Ответ №3:

Ваша оформленная функция ничего явно не возвращает — поэтому по умолчанию она возвращает None .

Вы можете записывать выходные данные перед печатью времени и возвращать в конце:

 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