Python decorators with parameters

Python Decorator with Arguments Python Decorator Tutorial with Example

If you want to take a really deep dive, you should read these exhaustive articles by Graham Dumpleton. However, if you intend to get started and getting better at reading/writing python decorators, this article should suffice. Everything is an object in python, even functions. A function can be assigned to a variable, passed to another function and can be returned from another function. Take a look at the example below:

[1]: def outer_function(): . print "1. This is outer function!" . def inner_function(): . print "2. This is inner function, inside outer function!" . print "3. This is outside inner function, inside outer function!" . return inner_function() . [2]: func_assign = outer_function() 1. This is outer function! 3. This is outside inner function, inside outer function! 2. This is inner function, inside outer function! 

Mark in the above execution, how the statement inside the inner function is printed at the last, consequential to inner_function being returned, at the end of outer_function, and the execution could be seen during the assignment. Python decorator are the function that receive a function as an argument and return another function as return value. The assumption for a decorator is that we will pass a function as argument and the signature of the inner function in the decorator must match the function to decorate.

Читайте также:  Функция код символа php

Function Decorator

Let us now, write a simple function decorator for ourselves. We will write a decorator that would measure the execution time of the function passed to it.

import time def timetest(input_func): def timed(*args, **kwargs): start_time = time.time() result = input_func(*args, **kwargs) end_time = time.time() print "Method Name - , Args - , Kwargs - , Execution Time - ".format( input_func.__name__, args, kwargs, end_time - start_time ) return result return timed @timetest def foobar(*args, **kwargs): time.sleep(0.3) print "inside foobar" print args, kwargs foobar(["hello, world"], foo=2, bar=5) inside foobar (['hello, world'],) 'foo': 2, 'bar': 5> Method Name - foobar, Args - (['hello, world'],), Kwargs - 'foo': 2, 'bar': 5>, Execution Time - 0.30296087265 

We passed the function foobar to decorator named timetest. Inside decorator, function foobar is referenced as variable input_func. The result, post execution of input_func is referred as result. Prepending @ to the name of the decorator, and writing the same above a function calls the decorator, and passes the function to the decorator(decorates).

Method Decorator

Method decorators allow overriding class properties by decorating, without having to find the calling function.

def method_decorator(method): def inner(city_instance): if city_instance.name == "SFO": print "Its a cool place to live in." else: method(city_instance) return inner class City(object): def __init__(self, name): self.name = name @method_decorator def print_test(self): print self.name p1 = City("SFO") p1.print_test() Its a cool place to live in. 

In the snippet shown above, we decorate the class method print_test. The method_decorator prints the name of the city, if the name of city instance is not SFO.

Class Decorators

If you want to create a callable returning another callable, the function decorator approach is easier. If you want the return to be a function, function decorators should be preferred, however if you want the decorator to return a custom object that does something different to what a function does, in that case a class decorator should be used. With a class, you can add methods and properties to the decorated callable object, or implement operations on them. You can create descriptors that act in a special way when placed in classes (e.g. classmethod, property)

class decoclass(object): def __init__(self, f): self.f = f def __call__(self, *args, **kwargs): # before f actions print 'decorator initialised' self.f(*args, **kwargs) print 'decorator terminated' # after f actions @decoclass def klass(): print 'class' klass() 

Chaining Decorators

The chaining of decorator is similar to how multiple inheritance can be used to construct classes We can write as many decorator as we want and include them one by one in decoration line with decoration syntax before the definition of function to be decorated.

def makebold(f): return lambda: "" + f() + "" def makeitalic(f): return lambda: "" + f() + "" @makebold @makeitalic def say(): return "Hello" print say() 

One thing should be kept in mind that the order of decorators we set matters. When you chain decorators, the order in which they are stacked is bottom to top.

Functools and Wraps

def decorator(func): """decorator docstring""" def inner_function(*args, **kwargs): """inner function docstring """ print func.__name__ + "was called" return func(*args, **kwargs) return inner_function @decorator def foobar(x): """foobar docstring""" return x**2 
print foobar.__name__ print foobar.__doc__ inner_function inner function docstring 

The above observation leads us to conclude that the function foobar is being replaced by inner_function. This means that we are losing information about the function which is being passed. functools.wraps comes to our rescue. It takes the function used in the decorator and adds the functionality of copying over the function name, docstring, arguemnets etc. Lets decorate without losing information:

from functools import wraps def wrapped_decorator(func): """wrapped decorator docstring""" @wraps(func) def inner_function(*args, **kwargs): """inner function docstring """ print func.__name__ + "was called" return func(*args, **kwargs) return inner_function @wrapped_decorator def foobar(x): """foobar docstring""" return x**2 print foobar.__name__ print foobar.__doc__ foobar foobar docstring 

The above implementation preserves the information about the funciton being passed to the decorator. How would you go about caching information inside a class based decorator? One of the ways of doing it, is listed here , would love to see more implementation, in comments.

Decorators with Arguments

from functools import wraps def decorator(arg1, arg2): def inner_function(function): @wraps(function) def wrapper(*args, **kwargs): print "Arguements passed to decorator %s and %s" % (arg1, arg2) function(*args, **kwargs) return wrapper return inner_function @decorator("arg1", "arg2") def print_args(*args): for arg in args: print arg print print_args(1, 2, 3) Arguements passed to decorator arg1 and arg2 1 2 3 

Class Based Decorators with Arguments

class ClassDecorator(object): def __init__(self, arg1, arg2): print "Arguements passed to decorator %s and %s" % (arg1, arg2) self.arg1 = arg1 self.arg2 = arg2 def __call__(self, foo, *args, **kwargs): def inner_func(*args, **kwargs): print "Args passed inside decorated function .%s and %s" % (self.arg1, self.arg2) return foo(*args, **kwargs) return inner_func @ClassDecorator("arg1", "arg2") def print_args(*args): for arg in args: print arg print_args(1, 2, 3) Arguements passed to decorator arg1 and arg2 Args passed inside decorated function .arg1 and arg2 1 2 3 

How would you go about implementing decorator with optional arguments? Try following this SO Post. You might want to further explore the Wrapt Library. The article originally appeared on Apcelent Tech Blog.

Источник

Декораторы с аргументами — Часть 3

Декораторы с аргументами

Это третья часть курса по декораторам в Python. В первых двух уроках было показано, как можно создать полезные декораторы, однако ни один из них не принимали аргументов. В данной статье мы рассмотрим процесс создания декораторов, которые принимают настраиваемые аргументы как обычные функции в Python.

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

Далее представлен список статей из данного курса:

  • Часть 1: Регистрация функции;
  • Часть 2: Изменение поведения функции;
  • Часть 3: Декораторы с аргументами (данная статья);
  • Часть 4: Классовые декораторы (скоро!)

Нужны ли декораторам аргументы?

Есть множество проблем, которые можно решить с помощью декораторов, и некоторые из них были рассмотрены в предыдущих уроках данного курса. Однако в некоторых ситуациях могут пригодиться и аргументы. Рассмотрим в качестве примера знаменитый декоратор app.route из веб-фреймворка Flask:

Назначение данного декоратора в регистрации декорированной функции в качестве обработчика для пути в Flask. Проблема в том, что во Flask вы можете определить множество путей, каждый из которых связан с другим URL, поэтому вам нужно каким-то образом сообщить декоратору, для какого URL вы регистрируете обработчик. Если бы мы не передали URL в качестве аргумента для декоратора, то не было бы возможности заставить этот декоратор маршрута работать так как мы задумали.

Создание декоратора с аргументами

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

Здесь видно, что декоратор my_decorator не принимает никаких аргументов, во время декорирования функции, но имплементация этого декоратора принимает аргумент f , через который Python передает ссылку на декорированную функцию.

Можно ожидать, что аргументы декоратора каким-то образом передаются в функцию вместе с аргументом f , но, к сожалению, Python всегда передает декорированную функцию как единственный аргумент функции декоратора. Ситуация усложняется еще тем, что у внутренней функции wrapped() есть «улавливающие все» аргументы, которые передаются непосредственно в декорированную функцию и никогда не должны смешиваться с аргументами, предназначенными для декоратора. По этой причине аргументы декоратора добавлять некуда.

Назовем рассматриваемые до сих пор декораторы «стандартными декораторами«. Мы видели, что стандартный декоратор — это функция, которая принимает декорированную функцию в качестве аргумента и возвращает другую функцию, которая занимает ее место. Используя приведенный выше пример с функцией my_function() , работу, которую выполняет стандартный декоратор, также можно реализовать в Python следующим образом:

Источник

Оцените статью