Когда оформляется функция, а когда не оформляется функция? - PullRequest
3 голосов
/ 23 сентября 2019

У меня есть декоратор, который записывает функции, присутствующие в моем скрипте:

registry=[]

def register(func):
    print('running register(%s)' % func)
    registry.append(func)
    return func

Затем у меня есть серия декорированных функций:

@register
def func1():
    print('running f1')

@register 
def func2():
    print('running f2')

Это работает после запуска скриптаprint (registry) возвращает:

[<function func1 at 0x0000000008433950>, <function func2 at 0x0000000008F06AE8>]

Однако вызывает функции по отдельности, например:

func1()

Возвращает только 'running f1': толькофункция, без отделки.

Я ожидал, что он вернет что-то вроде 'running register( func1) \n running func1'.

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

Большое спасибо.

Ответы [ 2 ]

4 голосов
/ 23 сентября 2019

Ваша register (декоратор) функция запускается только один раз при интерпретации кода.

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

def register(func):
    registry.append(func)
    print('adding register(%s)' % func)

    def wrap(*args, **kwargs):
        print('running register(%s)' % func)
        return func(*args, **kwargs)

    return wrap

Первая печать выполняется один раз, вторая перед каждым вызовом.Добавление аргументов сделает ваш декоратор более прозрачным.

2 голосов
/ 23 сентября 2019

То, что мы называем «декоратором», это просто функция более высокого порядка , а синтаксис @decorator - не более чем синтаксический сахар, так что:

@decorate
def func():
    pass

строго эквивалентно

def func():
    pass

func = decorate(func)

Как упомянул Гийом Десландес, если этот код находится на верхнем уровне вашего модуля или скрипта, декоратор вызывается только тогда, когда модуль или скрипт впервые загружаются средой выполнения.

В вашем случае функция декоратора register возвращает свой аргумент (функцию, к которой она применяется) без изменений, поэтому вызов «украшенной» функции будет работать точно , как если бы она никогда не выполняласьукрашенный.

Если вы хотите каким-либо образом изменить декорированную функцию (путем выполнения кода до и после исходной функции или чего-либо еще), вы должны вернуть новую функцию, которая «заменит»оригинал (но - обычно - с сохранением ссылки на исходную функцию, чтобы эта новая функция-обертка все еще могла вызывать оригинал), что обычно делается с использованием факта закрытия функций Python :

def decorate(fun):
   def wrapper(*args, **kw):
       res = fun(*args, **kw)
       print("function {} called with *{}, *{} returned {}".format(fun, args, kw, res)
       return res

   return wrapper



@decorate
def fun(a):
    return a * 2
...