Decorator - a template designed to connect additional behavior to an object. Used in many OOP languages: Java, C, PHP, JS. Python is no exception.
The task of decorators can be described by the following example. There is an object that performs a certain 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 major functions. Decorators are used to solve this problem. They extend the functionality, get rid of the creation of the second same class with additional methods.
Python decorators are wrappers that change the behavior of a function. The mutable object is a class, function, or other decorator. They must be applied very carefully with a clear understanding of what exactly needs to be achieved. Too much use of decorators makes the code harder to understand.
Decorator and function
A decorator in Python is a function that takes another function as an argument. Represents a block of code that returns some value.

Contains arguments, they will be used later and affect the return value. The output result can be of any type: list, tuple, function.
In Python, every function is an object, declared with the def keyword. The range of values is not specified by curly braces, but is indicated by the tab indentation. After the keyword, the name is indicated, the arguments are specified in brackets () after the name. Before the transition to a new line, the symbol “:” is placed.
In "Python" the body cannot be empty, it must contain a list of commands. If you need to leave this place, an empty pass statement is put.
def empty_func(): pass
This syntax applies to all functions except the anonymous one. Anonymous looks like this:
func=lambda x, y: x + y
Call:
func(1, 2,) returns 3
Call (second method):
(lambda x, y: x + y)(1, 2) returns 3
Decorators are called like this:
@decorator name def mutable_function body of mutable_function
The scheme of work 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.

Variables of the same type are listed separated by commas. There are several ways to assign values to variables specified in parameters.
- Regular.
- With keywords.
- Setting static values.
- Using positional elements.
When created, multiple arguments are specified in a specific order. When called, all values are specified in the parameters in the appropriate order.
def bigger(a, b): if a > b: print a else: print b
Correct call:
bigger(5, 6)
Wrong call:
bigger(3) bigger(12, 7, 3)
If the arguments used are keywords, they are called in an arbitrary order, since the value used determines the specific keyname.
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 via an assignment statement, as if the 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 stage of function creation is unknown, positional arguments are used. They can represent multiple 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,)
Similarway libraries of values with keys are transferred - using the symbol “”.
Variables specified in the body are local, used directly by the function itself. The global specifier is used to create a global variable.
def get_older(): global age age +=1
Recursion supported.
def fact(num): if num==0: return 1 else: return numfact(num - 1)
Decorating Methods
Functions and methods are syntactically similar.

The difference is that the function is only called by name.
func()
And the method is called through the “.” and enter the name of the method, where the first parameter is the parent.
object.func()
Thus, Python decorators for methods are created in the same way as for functions.
Here we created a decorator def method_friendly_decorator(method_to_decorate):
def wrapper(self, lie): lie=lie - 3return method_to_decorate(self, lie) return wrapper
F created a class here with methods that will be modified later^
class Lucy(object): def __init__(self): self.age=32 @method_friendly_decorator def sayYourAge(self, lie): print "I'm %s, how much would you give me?" % (self.age + lie) Lucy.sayYourAge(-32) I'm 26, how much would you give?
Format() is used here. 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 appear. Formatting is only available for inline elements. That is, the argument takes the place of the first curly braces {}, and the second - 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
To:
print("string {1} {0}").format(1, 2) string 2 1
Strings can be formatted using key names in the format format(arg1=value1, arg2=value2).
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, specify the index and name of the variable.
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 they are processing.
def decorator(function_to_decorate): def function(arg1, arg2): print "Look what I got:", arg1, arg2 return function
In this case, there is a @decorator that modifies function. Arguments are forwarded on the second line, passed to the mutable function_to_decorate.
Call:
@decorator def real_func(Peter, Ivanovich) print “My name is”, arg1, arg2
The screen will show:
Look what I got: Petr Ivanovich My name is Petr Ivanovich
Nested decorators
When one decorator is not enough, multiple levels of wrapping are implemented. When creating a nested decorator, each starts on a new line, the number of lines determines the level of complexity. Looks like this:
@AAA @BBB @CCC def function(…): pass:
Accordingly, AAA() takes BBB() as parameters, and it processes CCC().
def f(): pass: f=(AAA(BBB(CCC(function))):
Function passed in three different decorators, assigned to f(). Each of them returns its own result, which, in turn, processes the wrapper. You can notice that the last list decorator is the first one, 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())Prints "XXYYZZPython"
In this case, the wrapping logic is implemented by using def lambda:
lambda: 'XX' + arg()
Fn3() wraps myfunc, returns ZZPython instead of the previous Python string. Then the wrapped fn2() starts to work, which eventually returns the result of YYZZPython. ATat 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 with the interpreter and require additional modules to be imported to use them.
Staticmethod processes the argument function so that it becomes static and takes the static specifier.
class C: @staticmethod def f(arg1, arg2, …): static pass
Classmethod makes the processed function a class.
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. 6Call the class methods through the object. my_obj=MyClass() my_obj.method(1) my_obj.call_class_method()
Any function can be used as a decorator.
Replacing setters and getters
Getters, setters, deleters are assigned using the property class.
class property([fget[, fset[, fdel[, doc]]]])
Getters and setters in TypeScript are represented as follows:

Passing parameters to the Python class. Decoratorproperty has methods:
- fget - get attribute value;
- fset defines the attribute value;
- fdel removes;
- doc creates a description for the attribute. If doc is not assigned, a copy of fget()'s description is returned, if available.
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 decorator Pythonproperty:
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 setter, getter, delete methods, you can call one of them.
Features
There are a few things to keep in mind when working with a decorator:
- Using decorators slows down the function call a bit.
- A function once decorated cannot be undecorated. There are ways around this rule. You can create a decorator that can later be detached from the function. But this is not a good practice.
- Debugging can be difficult because the decorator wraps the function. The problem is solved using the functools module.
The functools module is a collection of methods that provideinteraction with other functions, it is also a Python decorator.

The useful method cmp_to_key(func) turns cmp() into key(). Both methods are for sorting a list, but the first one was removed in Python 3.0 and the second one was added in version 2. Lru_cache saves the latest calls to the cache. If maxsixe is set to none, the cache grows indefinitely. A dictionary is used to store frequently used queries. If the typed=true argument, then arguments of different types are cached separately. Accordingly, when typed=true, they are stored in a single list.
Total_ordering decorate the class that contains the comparison methods and add all the others.
Partial(func, args, keywords) returns a function that is called on the first argument in the method's parameter scope, passes a positional args that is given second, and a 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 the 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
Then the result is summed up with the third one, the result obtained from this is added to the next elements, etc.
Update_wrapper updates the wrapper to resemble the wrapped function. The arguments specify these two functions, copied andupdatable attributes.
assigned=WRAPPER_ASSIGNMENTS
The WRAPPER_ASSIGNMENTS tuple contains default values for __name__, __module__, __annotations__ and __doc__.
updated=WRAPPER_UPDATES
WRAPPER_UPDATES specifies the attributes that are being updated, the default is __dict__.
Wraps calls partial as a decorator.
Error handling decorator
The possibilities of decorators allow you to create a function that, when an error occurs, produces one result, if there is no error, another one.

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 is restarted.
@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 times an error will occur.
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()
This will output to the console:
Ok Ok Ok Error: Random!
Creating your own decorator
A decorator is a function that contains another function with corresponding methods.

Python decorator example:
def my_shiny_new_decorator(function_to_decorate): def the_wrapper_around_the_original_function(): print("I am the code that runs before the function is called") function_to_decorate() print("I am the code that runs after") 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 wrapped function will be handled. In this case, add:
print("I am the code that runs before the function is called") print("And I am the code that runs after")
My_shiny_new_decorator() returns the_wrapper_around_the_original_function() being decorated.
To work out a function, it wraps around 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 variable stand_alone_function.
def stand_alone_function(): print("I'm a simple standalone function, you wouldn't dare change me, would you?") stand_alone_function()
I am the code that will run until the function is called I am just a lonely function, you are notdare to change me? And I am the code that runs after
So you can see that the stand_alone_function that displayed one sentence now prints three sentences. This is done using a decorator.