Страницы

Поиск по вопросам

Показаны сообщения с ярлыком декоратор. Показать все сообщения
Показаны сообщения с ярлыком декоратор. Показать все сообщения

воскресенье, 9 февраля 2020 г.

Декоратор в python, мини вопрос

#python #декоратор


Допустим вот такой пример:

def wrap(f):
    def newf(*args):
        return f(*args)
    return newf

@wrap
def fun(a,b,c):
    print("Hello")

fun(a=1,b=2,c=3) #не работает, а нужно, чтоб работало


как такое реализовать? В newf указано *args, он не знает ни про какие a,b,c

Но если я напишу другую fun2(d,e) вместо fun вызову fun2(d=5,e=6), тоже должно работать
    


Ответы

Ответ 1



Используйте **kwargs def wrap(f): def newf(*args, **kwargs): return f(*args, **kwargs) return newf @wrap def fun(a,b,c): print("Hello") fun(a=1,b=2,c=3) Подробнее про синтаксис с ** можно прочитать, например, здесь https://djangofan.ru/args-kwargs-python

Ответ 2



Ошибка в том, что вы написали декоратор для неименованных параметров (кортеж), а потом вызываете функцию с помощью словаря: fun(a=1,b=2,c=3) # то же самое что и: fun(**{'a': 1, 'b': 2, 'c': 3}) # а ваш декоратор примает только: fun(*(1, 2, 3)) С помощью вашего декоратора допустимы следующие варианты: def wrap(f): def newf(*args): print("@", args) return f(*args) return newf @wrap def fun1(a, b, c): print("Hello" + str(a+b+c)) @wrap def fun2(a, b, c=3): print("Hello" + str(a+b+c)) @wrap def fun3(a, b=2, c=3): print("Hello" + str(a+b+c)) fun1(*(1, 2, 3)) fun2(*(1, 2)) fun3(*(1,)) Чтобы функция с декоратором могла принимать и кортежи и словари используйте: def wrap(f): def newf(*args, **kwargs): return f(*args, **kwargs) return newf

суббота, 8 февраля 2020 г.

Как обернуть функцию декоратором?

#python #python_32 #декоратор


Есть функция

def startInvRej(self, event): 
    if 2 < event.button < 4:
        self.canvCoor = []
        self.frameCoor = []
        print(event.x)
        print(event.xdata)
        self.frameCoor.append(event.x)
        self.canvCoor.append(float(event.xdata))
        print(self.frameCoor)
        print(self.canvCoor)


Как завернуть ее в декоратор, чтобы функция выполнялась с отладочным выводом (т.е.
с print'ами), если это необходимо, а иначе без него?



После Ваших ответов и недолгих раздумий было решено сделать таким образом: 

def decorator(func):
    def wrapped(self, event):
        print(event.x)
        print(event.xdata)
        func(self, event)
        print(self.frameCoor)
        print(self.canvCoor)
    return wrapped

@decorator
def startInvRej(self, event):
    if event.button ==3:
        self.canvCoor = []
        self.frameCoor = []
        self.frameCoor.append(event.x)
        self.canvCoor.append(float(event.xdata))


Насколько этот способ корректен? И объясните, пожалуйста, почему такой способ работает
несмотря на то, что я написал func(self, event), а не startInvRej(self, event)?
    


Ответы

Ответ 1



Прежде всего, рекомендую перейти на Python 3.3. Зачем быть не в тренде? Далее, не могу не порекомендовать встроенный модуль logging Пример из HOWTO: import logging logging.basicConfig(filename='example.log',level=logging.DEBUG) logging.debug('This message should go to the log file') logging.info('So should this') logging.warning('And this, too') Выведет в указанный файл: DEBUG:root:This message should go to the log file INFO:root:So should this WARNING:root:And this, too Запускаете своё приложение с ключом --log=INFO и наслажлаетесь фильтрованным логированием. Если задача именно в освоении декораторов, можно сделать супер-тупо: # Заглушка, которая не выводит ничего def log_print_stub( *args, **kwargs ): pass # глобальная переменная, с помощью которой # вызывается либо функция print для логгирования, # либо log_print_stub, чтобы ничего не делать log_print = log_print_stub # декоратор, изменяющий состояние глобальной переменной # перед вызовом декорируемой функции def logged(fn): def wrapped(*args, **kwargs): global log_print log_print = print result = fn(*args, **kwargs) log_print = log_print_stub return result return wrapped # пример: @logged def show_log( message ): print( "show_log" ) log_print( message, " ", message ) def dont_show_log( message ): print( "dont_show_log") log_print( message, " ", message ) # декорированная функция выведет "Видно Видно" show_log( "Видно" ) # не декорированная ничего дополнительно не делает dont_show_log( "Не видно" ) Считаю этот метод супер-тупым, так как глобальные переменные - зло. Кроме того, теоретически, этот метод непотокобезопасен.

Ответ 2



Если честно, тут очевидного применения декоратора я не вижу, может потому что не так часто ими пользуюсь, но всё же. А почему бы не попробовать вот так: def startInvRej(self, event, debug=False): if 2 < event.button < 4: self.canvCoor = [] self.frameCoor = [] if debug: print(event.x) print(event.xdata) self.frameCoor.append(event.x) self.canvCoor.append(float(event.xdata)) if debug: print(self.frameCoor) print(self.canvCoor) Там ведь достаточно очевидно и просто всё выглядит :)

среда, 5 февраля 2020 г.

Передать аргумент в декоратор

#python #декоратор


Привет!

Мне нужно передать простой аргумент(строку) в декоратор. Не понимаю, как это сделать.

def timing(function):
    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        time_start = timer()
        ret = function(*args, **kwargs)
        spent_time = round(timer() - time_start, 5)

        return ret, spent_time
    return wrapper


Пытался так:

def timing(function, string=''):
    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        time_start = timer()
        ret = function(*args, **kwargs)
        spent_time = round(timer() - time_start, 5)
        print string
        return ret, spent_time
    return wrapper


И вызывать так:

 @timing(string='getting statement')
    def check_statement(self):
        if bool(self.state):
            return


Вылетает с ошибкой:

@timing(message='')
TypeError: timing() takes exactly 2 arguments (1 given)


Пытался завернуть в еще один декоратор:

def timing(function, message):
    def clock(message):
        @functools.wraps(function)
        def wrapper(*args, **kwargs):
            time_start = timer()
            ret = function(*args, **kwargs)
            spent_time = round(timer() - time_start, 5)

        return wrapper# ret, spent_time
    return clock


Traceback:

    @timing(message='')
TypeError: timing() takes exactly 2 arguments (1 given)


Помогите пожалуйста сделать правильно
    


Ответы

Ответ 1



Вот: def decorator(s): def wrap(function): def called(*args, **kwargs): print(s, function, args, kwargs) return function(*args, **kwargs) return called return wrap @decorator('example') def f(num): print(num) f(1)

Ответ 2



Можно использовать functools.partial при указании декоратора у функции: @functools.partial(timing, string='getting statement') def check_statement(self): if bool(self.state): return

Ответ 3



def add_decor(func, add): def wrap(*args, **kwargs): return func(*args, **kwargs) + add return wrap def summ_(a, b=0): return a + b add_decor(summ_, add=100)(1, b=1) # 102 summ_add = add_decor(summ_, add=10) summ_add(1, b=1) # 12

понедельник, 30 декабря 2019 г.

Python декораторы

#python #python_3x #декоратор


Каким образом функция func() передалась как аргумент в функцию deco() ?

ps = input("Введите пароль:")

def test_pass(p):
    def deco(f):
        if p == "universe":
            return f
        else:
            return lambda: "Access denied"
    return deco

@test_pass(ps)
def func():
    return "Access approval"

print(func())

    


Ответы

Ответ 1



Грубо говоря, Python разворачивает конструкцию @test_pass(ps) def func(): ... в def func(): ... decorator = test_pass(ps) # В два шага для понятности func = decorator(func)

Ответ 2



В Питоне функции - это есть объекты. То есть мы можем сними делать всё, что сможем сделать с объектами. Например как в случае с декоратором. передавать объект функции как аргумент. так как func это есть объект функции, а func() вызов ps = 'any argument' decorator = test_pass(ps) # возращает объект внутренней функции deco. func = decorator(func) # вызывается deco(f)

суббота, 28 декабря 2019 г.

Декораторы Python

#python #python_3x #декоратор


Поясните, пожалуйста, следующее высказывание, желательно с примерами. Я новичок и
никак не могу осилить эти декораторы.


  Декораторы могут быть использованы для расширения возможностей функций из сторонних
библиотек (код которых мы не можем изменять).

    


Ответы

Ответ 1



Предположим, что в душе вы настоящий римлянин и вам неудобны варварские цифры, которые использует Python. Есть способ преобразовывать варварские цифры в римские и обратно - модуль roman. Но чтобы использовать его со стандартными функциями, такими как min и max, придётся писать код преобразований перед каждым вызовом. Типа: min_number = toRoman(min(fromRoman('VII'), fromRoman('X'))) Неудобно и неблаговидно! Поможет декоратор: from functools import wraps def romanize(f): @wraps(f) def wrapper(*args, **kwargs): return toRoman(f(tuple(fromRoman(i) for i in args), **kwargs)) return wrapper Декорируем интересующие функции min = romanize(min) max = romanize(max) И теперь в любом месте кода можем использовать их с римскими цифрами: min_number = min('VII', 'X')

четверг, 5 декабря 2019 г.

ЗАЧЕМ нужны декораторы? (НЕ как они работают, а ЗАЧЕМ)

#python #декоратор


Зачем нужны декораторы?

Кто читал классическое объяснение про декораторы:

# Декоратор - это функция, ожидающая ДРУГУЮ функцию в качестве параметра
def my_shiny_new_decorator(a_function_to_decorate):
    # Внутри себя декоратор определяет функцию-"обёртку".
    # Она будет (что бы вы думали?..) обёрнута вокруг декорируемой,
    # получая возможность исполнять произвольный код до и после неё.

    def the_wrapper_around_the_original_function():
        # Поместим здесь код, который мы хотим запускать ДО вызова
        # оригинальной функции
        print "Я - код, который отработает до вызова функции"

        # ВЫЗОВЕМ саму декорируемую функцию
        a_function_to_decorate()

        # А здесь поместим код, который мы хотим запускать ПОСЛЕ вызова
        # оригинальной функции
        print "А я - код, срабатывающий после"

    # На данный момент функция "a_function_to_decorate" НЕ ВЫЗЫВАЛАСЬ НИ РАЗУ

    # Теперь, вернём функцию-обёртку, которая содержит в себе
    # декорируемую функцию, и код, который необходимо выполнить до и после.
    # Всё просто!
    return the_wrapper_around_the_original_function

# Представим теперь, что у нас есть функция, которую мы не планируем больше трогать.
def a_stand_alone_function():
    print "Я простая одинокая функция, ты ведь не посмеешь меня изменять?.."

a_stand_alone_function()
# выведет: Я простая одинокая функция, ты ведь не посмеешь меня изменять?..

# Однако, чтобы изменить её поведение, мы можем декорировать её, то есть
# Просто передать декоратору, который обернет исходную функцию в любой код,
# который нам потребуется, и вернёт новую, готовую к использованию функцию:

a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#выведет:
# Я - код, который отработает до вызова функции
# Я простая одинокая функция, ты ведь не посмеешь меня изменять?..
# А я - код, срабатывающий после


Далее идет пассаж:


  Наверное, теперь мы бы хотели, чтобы каждый раз, во время вызова
  a_stand_alone_function, вместо неё вызывалась
  a_stand_alone_function_decorated. Нет ничего проще, просто перезапишем
  a_stand_alone_function функцией, которую нам вернул
  my_shiny_new_decorator:


a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#выведет:
# Я - код, который отработает до вызова функции
# Я простая одинокая функция, ты ведь не посмеешь меня изменять?..
# А я - код, срабатывающий после


Т.е. после этого пассажа - мы теряем возможность вызвать функцию в первоначальном
виде. Теперь она ВСЕГДА декорирована.

И вопрос, зачем тогда декоратор был нужен?

1) Почему бы (если мы все равно теряем первоначальную функцию) просто не переписать
изначальную функцию? (просто дописав в начале и в конце функции необходимые нам куски
кода). Вот так:

def a_stand_alone_function():
    print "Я - код, который отработает до вызова функции"
    print "Я простая одинокая функция, ты ведь не посмеешь меня изменять?.."
    print "А я - код, срабатывающий после"


или, если дополнительный код большой и должен лежать отдельно, то:

def a_stand_alone_function():
        pre_function()
        print "Я простая одинокая функция, ты ведь не посмеешь меня изменять?.."
        post_function()


2) Зачем вся эта свистопляска, вместо того, чтобы сделать простое решение, как в
1 вопросе?

3) Я бы еще понял декораторы, если бы была возможность вызывать как декорируемую,
так и изначальную функцию. Но декораторы, написанные с @ этого не позволяют. Зачем
декоратор затирает оригинальную функцию?

Буду очень благодарен за ответ с объяснениями и, может быть, ПОНЯТНЫМИ примерами
из реальной практики.
    


Ответы

Ответ 1



Например, в Django декораторы широко используются. Вместо того, чтобы изобретать велосипед и в каждой функции-контроллере, которая должна быть доступна только авторизованным пользователям, писать свой код проверки авторизации, просто декорируем её, соответствующим декоратором. А если нужно ещё гарантировать, что функция будет применяться только для запросов определённого типа? И что вся работа с БД в функции будет выполняться в транзакции? Если весь код тянуть в функцию, то вскоре её изначальный смысл просто потеряется в куче шаблонного кода. А она ведь ещё и не одна в модуле, и для каждой придётся написать этот шаблонный код, повторив его множество раз. Вместо того, чтобы просто написать: @require_POST @login_required @transaction_atomic def some_view(request): ...

Ответ 2



В дополнение к ответу @Sergey Gornostaev: Декораторы — по сути реализация аспектно-ориентированного программирования в Питоне. Вы определяете логику или функциональность отдельно от функций, которые будут пользоваться этой функциональностью. Возможно, эту логику разрабатывает для вас другая команда. Пример с логированием, который вы видели практически везде, или те же транзакции — один из возможных примеров аспекта-функциональности. Затем вы влёгкую можете подключить использование этого аспекта без ручного переписывания функций, просто добавлением или убиранием атрибута. В любом случае, атрибут — часть вашего исходного кода. Если вы хотите, чтобы недекорированная функция была доступна, вы можете просто не ставить декоратор. Или иметь отдельно недекорированную функцию, и отдельно её декорированный вариант, который вызывает недекорированный. Или дописать функциональность, которую вносил декоратор, вручную (но в этом случае вам нужно будет обновить ваш код, если в декораторе произойдут изменения, например, багфикс; это обычная проблема дубляжа кода.) Декоратор просто позволяет вам добавлять функциональность легко.

Ответ 3



# во первых ссылку на функцию можно получить из декоратора # во вторых теперь эти "куски кода в декораторе" дописать можно к любой функции, например логирование, проверка аргументов перед вызовом def decor(fn): '''декоратор''' def wrapper(*args, **kwargs): if all(args): out = fn(*args, **kwargs) with open('file.txt', 'a') as log: log.write('{} {} {} {}'.format(fn, args, kwargs, out)) print('декоратор', end=' ') return out return wrapper @decor def func(*args): '''@декоратор(изначальная функция)''' return sum(args) func(1, 2, 3) # вызывать как декорируемую original_fn = func.__closure__[0].cell_contents # func без декоратора original_fn(1, 2, 3) # вызывать как изначальную функцию # или import inspect original_fn = inspect.getclosurevars(func).nonlocals['fn'] original_fn(0, 2, 3) # если постоянный вызов дероратора не требуется, изначальную функцию не стоит декорировать @decor def func2(*args): '''изначальная функция''' return sum(args) func2(1, 2, 3) # вызывать как изначальную функцию decor(func2)(1, 2, 3) # вызывать как декорируемую

воскресенье, 14 апреля 2019 г.

Как обернуть функцию декоратором?

Есть функция
def startInvRej(self, event): if 2 < event.button < 4: self.canvCoor = [] self.frameCoor = [] print(event.x) print(event.xdata) self.frameCoor.append(event.x) self.canvCoor.append(float(event.xdata)) print(self.frameCoor) print(self.canvCoor)
Как завернуть ее в декоратор, чтобы функция выполнялась с отладочным выводом (т.е. с print'ами), если это необходимо, а иначе без него?

После Ваших ответов и недолгих раздумий было решено сделать таким образом:
def decorator(func): def wrapped(self, event): print(event.x) print(event.xdata) func(self, event) print(self.frameCoor) print(self.canvCoor) return wrapped
@decorator def startInvRej(self, event): if event.button ==3: self.canvCoor = [] self.frameCoor = [] self.frameCoor.append(event.x) self.canvCoor.append(float(event.xdata))
Насколько этот способ корректен? И объясните, пожалуйста, почему такой способ работает несмотря на то, что я написал func(self, event), а не startInvRej(self, event)?


Ответ

Прежде всего, рекомендую перейти на Python 3.3. Зачем быть не в тренде? Далее, не могу не порекомендовать встроенный модуль logging Пример из HOWTO import logging logging.basicConfig(filename='example.log',level=logging.DEBUG) logging.debug('This message should go to the log file') logging.info('So should this') logging.warning('And this, too') Выведет в указанный файл: DEBUG:root:This message should go to the log file INFO:root:So should this WARNING:root:And this, too Запускаете своё приложение с ключом --log=INFO и наслажлаетесь фильтрованным логированием. Если задача именно в освоении декораторов, можно сделать супер-тупо: # Заглушка, которая не выводит ничего def log_print_stub( *args, **kwargs ): pass
# глобальная переменная, с помощью которой # вызывается либо функция print для логгирования, # либо log_print_stub, чтобы ничего не делать log_print = log_print_stub
# декоратор, изменяющий состояние глобальной переменной # перед вызовом декорируемой функции def logged(fn): def wrapped(*args, **kwargs): global log_print log_print = print result = fn(*args, **kwargs) log_print = log_print_stub return result return wrapped
# пример:
@logged def show_log( message ): print( "show_log" ) log_print( message, " ", message )
def dont_show_log( message ): print( "dont_show_log") log_print( message, " ", message )
# декорированная функция выведет "Видно Видно" show_log( "Видно" ) # не декорированная ничего дополнительно не делает dont_show_log( "Не видно" ) Считаю этот метод супер-тупым, так как глобальные переменные - зло. Кроме того, теоретически, этот метод непотокобезопасен.

пятница, 14 декабря 2018 г.

Python декораторы

Каким образом функция func() передалась как аргумент в функцию deco() ?
ps = input("Введите пароль:")
def test_pass(p): def deco(f): if p == "universe": return f else: return lambda: "Access denied" return deco
@test_pass(ps) def func(): return "Access approval"
print(func())


Ответ

Грубо говоря, Python разворачивает конструкцию
@test_pass(ps) def func(): ...
в
def func(): ...
decorator = test_pass(ps) # В два шага для понятности func = decorator(func)