Функция-оболочка Python, принимающая аргументы внутри декоратора

Я пытаюсь написать декораторы Python, и у меня возникают проблемы с пониманием того, как внутренняя оболочка принимает аргументы. У меня есть здесь:

import time

def timing_function(some_function):
    def wrapper():
        t1 = time.time()
        some_function()
        t2 = time.time()
        return "Time it took to run: " + str((t2-t1)) + "\n"
    return wrapper

@timing_function
def my_function(x):
    return x * x

my_function(6)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-fe2786a2753c> in <module>()
----> 1 my_function(6)

TypeError: wrapper() takes no arguments (1 given)

Что немного отличается от примера:

import time

def timing_function(some_function):

    """
    Outputs the time a function takes
    to execute.
    """

    def wrapper():
        t1 = time.time()
        some_function()
        t2 = time.time()
        return "Time it took to run the function: " + str((t2-t1)) + "\n"
    return wrapper

@timing_function
def my_function():
    num_list = []
    for x in (range(0,10000)):
        num_list.append(x)
    return "\nSum of all the numbers: " +str((sum(num_list)))


print my_function()

Time it took to run the function: 0.0

Кажется, проблема заключается в аргументе «x». Я пробовал передать обертку *args, но это тоже не сработало. Мои вопросы

  1. Как правильно разрешить аргументы в этой простой оболочке? Спасибо

  2. Почему все примеры декораторов, которые я видел, всегда имеют внутреннюю функцию, нельзя ли написать декоратор как одну функцию?

Спасибо


person codyc4321    schedule 18.06.2015    source источник
comment
используйте *args, **kwargs в своей подписи оболочки, чтобы def wrapper() стало def wrapper(*args, **kwargs). Таким образом, вы можете использовать декоратор для любого метода или функции.   -  person Christopher Pearson    schedule 18.06.2015


Ответы (2)


  1. Как правильно разрешить аргументы в этой простой оболочке? Спасибо

Вам нужно передать аргумент от my_function к wrapper, т.е.:

def wrapper(x):

Если вы хотите, чтобы он мог обрабатывать гораздо больше функций в целом, вам нужно будет сделать что-то вроде:

def wrapper(*args, **kwargs):

Но тогда ваша логика в декораторе также должна иметь возможность обрабатывать args и kwargs в общем.

  1. Почему все примеры декораторов, которые я видел, всегда имеют внутреннюю функцию, нельзя ли написать декоратор как одну функцию?

Потому что декоратор — это функция, которая принимает функцию в качестве аргумента и возвращает функцию, которая выполняется как оболочка исходной функции. На самом деле декораторы часто записываются как три функции:

from functools import wraps

def somedec(somearg, someopt=None):
    def somedec_outer(fn):
        @wraps(fn)
        def somedec_inner(*args, **kwargs):
            # do stuff with somearg, someopt, args and kwargs
            response = fn(*args, **kwargs)
            return response
        return somedec_inner
    return somedec_outer

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

@somedec(30.0, 'foobarbaz')
def somefn(a, b, c):
    return a + b + c

@somedec(15.0, 'quxjinzop')
def otherfn(d, e, f):
    return d - e - f

functools.wraps сделает украшенную функцию похожей на исходную функцию для интерпретатора Python. Это полезно для ведения журнала и отладки и т. д., и это лучшая практика для использования при создании декораторов.

person mVChr    schedule 18.06.2015
comment
Как def wrapper(*args, **kwargs) получить необходимые аргументы, если функция time_function принимает (some_function), а не (some_function, *args, **kwargs) - person codyc4321; 18.06.2015
comment
Потому что декоратор, по сути, делает следующее: my_function = timing_function(my_function) поэтому, даже если кажется, что вы вызываете my_function, на самом деле вы вызываете wrapper, поскольку timing_function возвращает wrapper. Таким образом, новый my_function равен wrapper, и поэтому wrapper получает аргументы и kwargs, которые вы передаете my_function. - person mVChr; 18.06.2015
comment
# 2 было важнее понять, ти все - person codyc4321; 18.06.2015
comment
Черт, спасибо, @mVChr, я пытался понять, откуда враппер берет свои аргументы и kwargs, и твой комментарий взорвал мне мозг! - person AbdurRehman Khan; 04.05.2020

Вам нужно добавить аргумент в оболочку, а затем some_function:

def timing_function(some_function):
    def wrapper(arg): 
        t1 = time.time()
        some_function(arg)
        t2 = time.time()
        return "Time it took to run: " + str((t2-t1)) + "\n"
    return wrapper

Если вы хотите использовать декоратор для разных функций, используйте *args и ** kwargs, которые также будут работать для функций, не принимающих аргументы:

def timing_function(some_function):
    def wrapper(*args,**kwargs):
        t1 = time.time()
        some_function(*args,**kwargs)
        t2 = time.time()
        return "Time it took to run: " + str((t2-t1)) + "\n"
    return wrapper

Кстати, модуль timeit может оказаться полезным, если вы хотите код, если у вас установлен ipython, вы можете просто использовать timeit your_function.

Почему все примеры декораторов, которые я видел, всегда имеют внутреннюю функцию, нельзя ли написать декоратор как одну функцию?

Нет, Декоратор в Python — это вызываемый объект Python, который используется для изменения определения функции, метода или класса. Исходный объект, который будет изменен, передается декоратору в качестве аргумента. Декоратор возвращает измененный объект, например. модифицированная функция, привязанная к имени, используемому в определении, есть еще примеры здесь

person Padraic Cunningham    schedule 18.06.2015
comment
хотя OP использовал один аргумент, это похоже на общую функцию синхронизации, поэтому def wrapper(*args, **kwargs) может быть более подходящей подписью. - person mehtunguh; 18.06.2015
comment
@mehtunguh, да, я просто использовал код ОП в качестве примера. Моя сеть вышла из строя, поэтому я не мог отредактировать, чтобы добавить общий пример. - person Padraic Cunningham; 18.06.2015