What are Python decorators?

Decorator - a template designed to connect additional behavior to the object. Used in many OOP languages: Java, C #, PHP, JS. Python is no exception.

The task of decorators can be described in the following example. There is an object that performs some function, during the development of the program it is required to add some additional functionality to it. It can be performed before or after, or even during basic functions. To solve this problem, decorators are used. They expand the functionality, eliminating the need to create a second of the same class with additional methods.

Python decorators are a kind of wrapper that changes the behavior of a function. A class, function, or other decorator is used as a mutable object. They should be applied very carefully with a clear understanding of what exactly needs to be achieved. Using decorators too often makes code more difficult to understand.

Decorator and Function

Decorators in Python are a function that uses another function as an argument. It is a block of code that returns some value.

Python decorator

Contains arguments, they will be used in the future and will affect the return value. The result, which is obtained at the output, can be of any type: list, tuple, function.

In Python, every function is an object; it is declared using the def keyword. The range of values ​​is not specified by curly brackets, but is indicated by the tab indent. The name is indicated after the keyword, the arguments are given in brackets () after the name. Before going to a new line, the “:” symbol is put.

In Python, the body cannot be empty; it must necessarily contain a list of commands. If you need to leave this place, an empty pass statement is set.

def empty_func(): pass 

This syntax applies to all functions except anonymous. Anonymous looks like this:

 func = lambda x, y: x + y 

Call:

 func(1, 2,) # 3 

Call (second method):

 (lambda x, y: x + y)(1, 2) # 3 

Decorators are called like this:

 @  def _  _ 

The operation scheme is described by the following code:

 def decorator(change_funct): def return_func1(): print “code before” change_funct() print “code after” return return_func1 

Accordingly, the call is as follows:

 @decorator def retrurn_func1(): print new_change 

Function Arguments

Python decorator arguments pass any data type.

Decorator in simple words

Variables of the same type are listed with a comma. There are several ways to assign values ​​to variables specified in the parameters.

  1. Normal.
  2. Using keywords.
  3. Setting static values.
  4. Using positional elements.

When creating, several arguments are specified in a specific order. When called in the parameters, all values ​​are indicated in the corresponding order.

 def bigger(a,b): if a > b: print a else: print b 

Correct call:

 bigger(5,6) 

Invalid call:

 bigger(3) bigger(12,7,3) 

If the arguments used are keywords, they are called in random order, since the value used defines a specific key-name.

 def person(name, age): print name, "is", age, "years old" person(age=23, name="John") 

Static values ​​of variables are created together with the function through the assignment operator as if initialization took place in the body.

 def space(planet_name, center="Star"): print(planet_name, "is orbiting a", center) space("Mars") 

When the number of arguments at the function creation stage is unknown, positional arguments are used. They can indicate several variables of the same type or a list:

 def func(*args): return args func(1, 2, 3, 'abc') # (1, 2, 3, 'abc') func(1) #(1,) 

In a similar way, value libraries with keys are transferred using the “**” symbol.

The variables specified in the body are local, they are used directly by the function itself. The global specifier is used to create a global variable.

 def get_older(): global age age += 1 

Recursion is supported.

 def fact(num): if num == 0: return 1 else: return num * fact(num - 1) 

Dressing Methods

Functions and methods are syntactically similar.

Decorator function

The difference is that the function is called only by name.

 func() 

And the method is called through the “.” Operator and the method name is entered, where the first parameter is the parent.

 object.func() 

Thus, Python decorators for methods are created in the same way as for a function.

Here we created the def method_friendly_decorator (method_to_decorate) decorator:

 def wrapper(self, lie): lie = lie - 3 # return method_to_decorate(self, lie) return wrapper 

F here we created a class with methods that will be modified later ^

 class Lucy(object): def __init__(self): self.age = 32 @method_friendly_decorator def sayYourAge(self, lie): print " %s,     ?" % (self.age + lie) Lucy.sayYourAge(-32) # 26,     ? 

Here format () is used. Intended for formatting strings, used in this form:

 print(“string {} {}).format(1, 2) #string 1 2​ 

In this example, two digits are specified in the format () arguments: 1, 2. They replace the {} characters in the order in which they are located. Formatting is available exclusively for string elements. That is, the argument takes the place of the first curly braces {}, and the second - of the second, respectively. The format method provides the ability to change the order in which values ​​are inserted. This is done through indexes.

If:

 print(“string {} {}).format(1, 2) #string 1 2 

That:

 print(“string {1} {0}).format(1, 2) #string 2 1 

Formatting strings through key names in the format format (arg1 = value1, arg2 = value2) is allowed.

 print(“string {arg1} {arg2}).format(arg1 = 1, arg2 = 2) #string 1 2 

You can use a mixed system - when with two arguments only one of them has a static value. To pass values, an index and a variable name are indicated.

 print(“string {arg1} {1}).format(arg1 = 1, 2) #string 1 2 

Decorators with Arguments

Python decorators can be passed arguments that subsequently modify the function being processed.

 def decorator(function_to_decorate): def function(arg1, arg2): print ",   :", arg1, arg2 return function 

In this case, there is @decorator that modifies the function. Arguments are thrown in the second line, transferred to variable function_to_decorate.

Call:

 @decorator def real_func(, ) print “ ”, arg1, arg2 

The following will appear on the screen:

 ,   : ,      

Nested Decorators

When one decorator is not enough, several levels of wrapping are implemented. When creating a nested decorator, each begins with a new line, the number of lines determines the level of complexity. Looks like that:

 @AAA @BBB @CCC def function(...): pass: 

Accordingly, AAA () accepts BBB () in the parameters, and it processes CCC ().

 def f(): pass: f = (AAA(BBB(CCC(function))): 

Function is passed by three different decorators, assigned to f (). Each of them returns its own result, which, in turn, processes the wrapper. You may notice that the last list decorator is the first; it starts processing function ().

In Python, class decorators look the same.

 @firsdecorator @seconddecorator class CDC: pass: C = firstdecorator(seconddecorator(CDC)) XX = C() def fn1(arg): return lambda: 'XX' + arg() def fn2(arg): return lambda: 'YY' + arg() def fn3(arg): return lambda: 'ZZ' + arg() @fn1 @fn2 @fn3 def myfunc(): # myfunc = fn1(fn2(fn3(myfunc))) return 'Python' print(myfunc()) #  "XXYYZZPython" 

In this case, the implementation of the wrapping logic occurs by using def lambda:

 lambda: 'XX' + arg() 

Fn3 () wraps myfunc, returns ZZPython instead of the previous Python line. Then the wrapped fn2 () starts working, which ultimately returns the result of YYZZPython. At the end, fn1 () processes myfunc () and returns the final result - the string XXYYZZPython.

Built-in Decorators

There are built-in Python function decorators. They come complete with an interpreter; to use them, you need to import additional modules.

Staticmethod processes the argument function so that it becomes static and accepts the static specifier.

 class C: @staticmethod def f(arg1, arg2, ...): #static pass 

Classmethod makes a class out of the function being processed.

 class MyClass: @classmethod def method(cls, arg): print('%s classmethod. %d' % (cls.__name__, arg)) @classmethod def call_original_method(cls): cls.method(5) def call_class_method(self): self.method(10) class MySubclass(MyClass): @classmethod def call_original_method(cls): cls.method(6) MyClass.method(0) # MyClass classmethod. 0 MyClass.call_original_method() # MyClass classmethod. 5 MySubclass.method(0) # MySubclass classmethod. 0 MySubclass.call_original_method() # MySubclass classmethod. 6 #     . my_obj = MyClass() my_obj.method(1) my_obj.call_class_method() 

Any function can be used as a decorator.

Replacing setters and getters

Using the property class getters, setters, deletters are assigned.

 class property([fget[, fset[, fdel[, doc]]]]) 

TypeScript getters and setters are represented as follows:

Getters and Setters

Parameters are passed to the Python class. The property decorator has methods:

  • fget - gets the attribute value;
  • fset defines the value of the attribute;
  • fdel removes;
  • doc creates a description for the attribute. If no doc is assigned, a copy of the description of fget (), if any, is returned.
 class C(object): def __init__(self): self._x = None def getx(self): return self._x def setx(self, value): self._x = value def delx(self): del self._x x = property(getx, setx, delx, "I'm the 'x' property.") 

Using a function as a Pythonproperty decorator:

 class C(object): def __init__(self): self._x = None @property def x(self): """I'm the 'x' property.""" return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x 

Property created a decorator from function x. Since all decorators have built-in methods setter, getter, deletter, you can call one of them.

Features

When working with a decorator, you need to consider some features:

  1. Using decorators slows down a function call a bit.
  2. Once a decoded function cannot be undecorated. There are ways around this rule. You can create a decorator that can later be disconnected from the function. But this is not a very good practice.
  3. Because the decorator wraps a function, debugging can be complicated. The problem is solved using the functools module.

The functools module is a collection of methods that provides interaction with other functions; it is also a Python decorator.

Python functuls

The useful cmp_to_key (func) method turns cmp () into key (). Both methods are designed to sort the list, but the first is removed in Python 3.0, and the second is added in version 2. Lru_cache saves the last calls to the cache. If maxsixe is specified as none, the cache size increases indefinitely. A dictionary is used to store frequently used queries. If the argument is typed = true, then arguments of different types are cached separately. Accordingly, with typed = true, they are saved in a single list.

Total_ordering decorates a class that contains comparison methods, and adds all the rest.

Partial (func, * args, ** keywords) returns a function that is called by the first argument from the method parameter area, passes the positional * args, which is given by the second, and named kwargs.

Reduce works like this:

 reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) 

Equivalent to:

 ((((1+2)+3)+4)+5) 

Reduce applies the specified function sequentially to pairs of elements specified in the ** keywords list, or to all * args elements. Thus, in the example above, the first two elements are processed using the lambda function:

 1+2 

Next, the result is summarized with the third, the result obtained from this is added to the following elements, etc.

Update_wrapper updates the shell so that it resembles a wrapped function. The arguments indicate these two functions, the copied and updated attributes.

 assigned=WRAPPER_ASSIGNMENTS 

The WRAPPER_ASSIGNMENTS tuple contains the default values ​​__name__, __module__, __annotations__, and __doc__.

 updated=WRAPPER_UPDATES 

In WRAPPER_UPDATES, the attributes that are updated are specified, by default it is __dict__.

Wraps calls partial as a decorator.

Error Processing Decorator

The capabilities of decorators allow you to create a function that when an error occurs produces one result, if there is no error, another.

Error in python

Implementation:

 import functools def retry(func): @functools.wraps(func) def wrapper(*args, **kwargs): while True: try: return func(*args, **kwargs) except Exception: pass return wrapper​ 

It can be seen that in case of exceptions, the function starts again.

 @retry def do_something_unreliable(): if random.randint(0, 10) > 1: raise IOError("Broken sauce, everything is hosed!!!111one") else: return "Awesome sauce!" print(do_something_unreliable()) 

This code means that 9 out of 11 cases will cause an error.

 def my_decorator(fn): def wrapped(): try: return fn() except Exception as e: print("Error:", e) return wrapped @my_decorator def my_func(): import random while True: if random.randint(0, 4) == 0: raise Exception('Random!') print('Ok') my_func() 

Here, the console will display:

 Ok Ok Ok Error: Random! 

Create your own decorator

A decorator is a function, inside which is another function with the appropriate methods.

Decorators in Python

Python decorator example:

 def my_shiny_new_decorator(function_to_decorate): def the_wrapper_around_the_original_function(): print(" - ,     ") function_to_decorate() print("  - ,  ") return the_wrapper_around_the_original_function 

In this code, the decorator is my_shiny_new_decorator (). Further decorates function_to_decorate () from the parameter area. the_wrapper_around_the_original_function is an example of how the decorated function will be processed. In this case, it is added:

 print(" - ,     ") print("  - ,  ") 

My_shiny_new_decorator () returns the decorated the_wrapper_around_the_original_function ().

To work out any function, it turns into a decorator.

 stand_alone_function = my_shiny_new_decorator(stand_alone_function) 

In this case, the decorated function is stand_alone_function, the decorator is my_shiny_new_decorator. The value is assigned to the stand_alone_function variable.

 def stand_alone_function(): print("   ,      ?") stand_alone_function() 
  - ,         ,      ?   - ,   

Thus, it can be seen that stand_alone_function, which displayed one sentence, now displays three sentences. This is done using a decorator.

Source: https://habr.com/ru/post/K17541/


All Articles