How To Create A Decorator In Python
half dozen. Decorators and Decoration
By Bernd Klein. Last modified: 01 Feb 2022.
Introduction
Decorators belong most probably to the near beautiful and most powerful design possibilities in Python, but at the same time the concept is considered by many as complicated to get into. To be precise, the usage of decorators is very like shooting fish in a barrel, just writing decorators tin be complicated, especially if you are not experienced with decorators and some functional programming concepts.
Even though it is the same underlying concept, we have two different kinds of decorators in Python:
- Role decorators
- Class decorators
A decorator in Python is any callable Python object that is used to alter a part or a class. A reference to a function "func" or a class "C" is passed to a decorator and the decorator returns a modified function or class. The modified functions or classes usually incorporate calls to the original part "func" or form "C".
You may likewise consult our affiliate on memoization with decorators.
If you like the paradigm above and if yous are too interested in image processing with Python, Numpy, Scipy and Matplotlib, you will definitely like our chapter on Paradigm Processing Techniques, it explains the whole process of the making-of of our decorator and at sign motion-picture show!
First Steps to Decorators
We know from our various Python preparation classes that at that place are some points in the definitions of decorators, where many beginners get stuck.
Therefore, we will innovate decorators by repeating some important aspects of functions. First you take to know or remember that function names are references to functions and that we can assign multiple names to the same function:
def succ ( x ): return 10 + 1 successor = succ successor ( 10 )
OUTPUT:
OUTPUT:
This means that we have two names, i.eastward. "succ" and "successor" for the aforementioned function. The side by side important fact is that we can delete either "succ" or "successor" without deleting the function itself.
OUTPUT:
Functions inside Functions
The concept of having or defining functions inside of a function is completely new to C or C++ programmers:
def f (): def thou (): print ( "Hullo, it's me 'g'" ) print ( "Cheers for calling me" ) print ( "This is the role 'f'" ) print ( "I am calling 'thousand' now:" ) g () f ()
OUTPUT:
This is the part 'f' I am calling 'g' now: How-do-you-do, it'due south me 'g' Cheers for calling me
Another example using "proper" return statements in the functions:
def temperature ( t ): def celsius2fahrenheit ( 10 ): return 9 * 10 / v + 32 issue = "It'south " + str ( celsius2fahrenheit ( t )) + " degrees!" return effect print ( temperature ( xx ))
OUTPUT:
The following example is about the factorial function, which we previously defined as follows:
def factorial ( north ): """ calculates the factorial of due north, due north should be an integer and n <= 0 """ if n == 0 : return 1 else : return n * factorial ( north - i )
What happens if someone passes a negative value or a float number to this function? It will never end. You might go the idea to check that as follows:
def factorial ( n ): """ calculates the factorial of n, n should be an integer and due north <= 0 """ if type ( n ) == int and north >= 0 : if n == 0 : render 1 else : render northward * factorial ( n - 1 ) else : raise TypeError ( "n has to be a positive integer or zero" )
If y'all call this function with 4 '' for example, i.e.
factorial (iv) '', the first thing that is checked is whether it is my positive integer. In principle, this makes sense. The "problem" at present appears in the recursion footstep. Now factorial (3) '' is chosen. This call and all others also cheque whether it is a positive whole number. But this is unnecessary: If you lot subtract the value
one '' from a positive whole number, you get a positive whole number or `` 0 '' again. So both well-divers argument values for our part.
With a nested function (local role) ane can solve this problem elegantly:
def factorial ( n ): """ calculates the factorial of n, n should be an integer and n <= 0 """ def inner_factorial ( n ): if n == 0 : return i else : return n * inner_factorial ( due north - one ) if type ( n ) == int and northward >= 0 : return inner_factorial ( n ) else : raise TypeError ( "n should be a positve int or 0" )
Functions as Parameters
If you solely look at the previous examples, this doesn't seem to be very useful. It gets useful in combination with 2 farther powerful possibilities of Python functions. Due to the fact that every parameter of a function is a reference to an object and functions are objects besides, we can pass functions - or better "references to functions" - as parameters to a role. Nosotros will demonstrate this in the next uncomplicated example:
def thousand (): impress ( "Hi, it's me 'm'" ) print ( "Thanks for calling me" ) def f ( func ): print ( "Hi, it's me 'f'" ) print ( "I will phone call 'func' now" ) func () f ( yard )
OUTPUT:
Hi, it'southward me 'f' I will call 'func' now Hi, it'south me 'g' Thanks for calling me
Y'all may not exist satisfied with the output. 'f' should write that information technology calls '1000' and not 'func'. Of course, we need to know what the 'existent' name of func is. For this purpose, we can use the attribute __name__
, as it contains this proper name:
def g (): impress ( "Hi, it'south me '1000'" ) print ( "Thanks for calling me" ) def f ( func ): print ( "Hi, it's me 'f'" ) print ( "I will call 'func' now" ) func () print ( "func's real proper name is " + func . __name__ ) f ( g )
OUTPUT:
Hi, information technology'due south me 'f' I will call 'func' now Hi, it's me 'm' Thanks for calling me func's real proper name is chiliad
The output explains what'due south going on once more. Another example:
import math def foo ( func ): print ( "The function " + func . __name__ + " was passed to foo" ) res = 0 for x in [ 1 , ii , ii.v ]: res += func ( 10 ) return res impress ( foo ( math . sin )) print ( foo ( math . cos ))
OUTPUT:
The role sin was passed to foo ii.3492405557375347 The function cos was passed to foo -0.6769881462259364
Functions returning Functions
The output of a function is also a reference to an object. Therefore functions tin can return references to function objects.
def f ( x ): def g ( y ): return y + x + 3 return g nf1 = f ( one ) nf2 = f ( 3 ) print ( nf1 ( ane )) print ( nf2 ( 1 ))
OUTPUT:
The previous case looks very artificial and absolutely useless. We volition present now another language oriented example, which shows a more practical touch. Okay, still not a office which is useful the way it is. We write a function with the nearly self-explanatory name greeting_func_gen
. So this function returns (or generates) functions which can be used to create people in different languages, i.e. German, French, Italian, Turkish, and Greek:
def greeting_func_gen ( lang ): def customized_greeting ( name ): if lang == "de" : # German phrase = "Guten Morgen " elif lang == "fr" : # French phrase = "Bonjour " elif lang == "information technology" : # Italian phrase = "Buongiorno " elif lang == "tr" : # Turkish phrase = "Günaydın " elif lang == "gr" : # Greek phrase = "Καλημερα " else : phrase = "Hi " return phrase + name + "!" return customized_greeting say_hi = greeting_func_gen ( "tr" ) print ( say_hi ( "Gülay" )) # this Turkish name means "rose moon" past the way
OUTPUT:
It is getting more than useful and at the same time more mathematically oriented in the post-obit example. We will implement a polynomial "factory" function now. We will start with writing a version which tin create polynomials of degree two.
$$p(10) = ax^2 + bx + c$$
The Python implementation equally a polynomial factory office tin can be written similar this:
def polynomial_creator ( a , b , c ): def polynomial ( ten ): return a * x ** 2 + b * x + c render polynomial p1 = polynomial_creator ( 2 , 3 , - 1 ) p2 = polynomial_creator ( - 1 , 2 , 1 ) for 10 in range ( - ii , 2 , 1 ): print ( x , p1 ( x ), p2 ( x ))
OUTPUT:
-2 i -seven -1 -ii -two 0 -1 1 1 4 2
We can generalize our factory function and then that it tin can work for polynomials of capricious caste:
$$\sum_{k=0}^{n} a_{grand} \cdot x^{thou} = a_{n} \cdot x^{northward} + a_{northward-one} \cdot ten^{northward-i} + ... + a_{2} \cdot x^{ii} + a_{ane} \cdot x + a_{0} $$
def polynomial_creator ( * coefficients ): """ coefficients are in the grade a_n, ... a_1, a_0 """ def polynomial ( x ): res = 0 for index , coeff in enumerate ( coefficients [:: - 1 ]): res += coeff * 10 ** index return res return polynomial p1 = polynomial_creator ( 4 ) p2 = polynomial_creator ( two , 4 ) p3 = polynomial_creator ( i , 8 , - 1 , 3 , 2 ) p4 = polynomial_creator ( - ane , 2 , 1 ) for ten in range ( - 2 , two , 1 ): print ( ten , p1 ( x ), p2 ( 10 ), p3 ( x ), p4 ( ten ))
OUTPUT:
-2 iv 0 -56 -7 -one 4 2 -9 -2 0 iv iv two 1 1 iv 6 13 2
The function p3 implements, for example, the following polynomial:
$$p_3(x) = ten^{4} + eight \cdot x^{3} - x^{2} + 3 \cdot x + 2 $$
The polynomial function within of our decorator polynomial_creator can exist implemented more efficiently. We can factorize information technology in a way then that it doesn't demand any exponentiation.
Factorized version of a full general polynomial without exponentiation:
$$res = (...(a_{n} \cdot x + a_{north-1}) \cdot 10 + ... + a_{ane}) \cdot x + a_{0}$$
Implementation of our polynomial creator decorator avoiding exponentiation:
def polynomial_creator ( * coeffs ): """ coefficients are in the form a_n, a_n_1, ... a_1, a_0 """ def polynomial ( x ): res = coeffs [ 0 ] for i in range ( one , len ( coeffs )): res = res * x + coeffs [ i ] return res render polynomial p1 = polynomial_creator ( 4 ) p2 = polynomial_creator ( two , four ) p3 = polynomial_creator ( 1 , 8 , - 1 , 3 , ii ) p4 = polynomial_creator ( - 1 , ii , 1 ) for 10 in range ( - 2 , 2 , 1 ): impress ( x , p1 ( x ), p2 ( x ), p3 ( x ), p4 ( x ))
OUTPUT:
-2 4 0 -56 -7 -1 4 2 -9 -2 0 4 4 2 1 ane iv 6 13 2
If you want to acquire more than most polynomials and how to create a polynomial grade, y'all tin continue with our chapter on Polynomials.
A Simple Decorator
Now we have everything ready to define our first unproblematic decorator:
def our_decorator ( func ): def function_wrapper ( x ): print ( "Earlier calling " + func . __name__ ) func ( x ) print ( "Later on calling " + func . __name__ ) return function_wrapper def foo ( x ): print ( "Hi, foo has been called with " + str ( x )) print ( "We call foo before ornamentation:" ) foo ( "Hi" ) print ( "We now decorate foo with f:" ) foo = our_decorator ( foo ) print ( "We call foo after ornament:" ) foo ( 42 )
OUTPUT:
Nosotros call foo before decoration: Hi, foo has been called with Howdy We now decorate foo with f: We call foo afterward ornamentation: Earlier calling foo Hi, foo has been called with 42 After calling foo
If you expect at the output of the previous program, you tin can see what's going on. Subsequently the decoration "foo = our_decorator(foo)", foo is a reference to the 'function_wrapper'. 'foo' will be called inside of 'function_wrapper', merely before and after the call some additional code will be executed, i.e. in our case ii print functions.
Alive Python grooming
Upcoming online Courses
Enrol here
The Usual Syntax for Decorators in Python
The decoration in Python is usually not performed in the way we did it in our previous instance, fifty-fifty though the notation foo = our_decorator(foo)
is catchy and easy to grasp. This is the reason, why we used it! You can too see a design trouble in our previous approach. "foo" existed in the aforementioned program in two versions, before decoration and subsequently decoration.
We will exercise a proper decoration now. The decoration occurrs in the line before the role header. The "@" is followed by the decorator function name.
We will rewrite now our initial case. Instead of writing the argument
foo = our_decorator(foo)
we tin write
@our_decorator
But this line has to exist directly positioned in front of the decorated function. The complete example looks like this now:
def our_decorator ( func ): def function_wrapper ( x ): print ( "Before calling " + func . __name__ ) func ( x ) print ( "Afterward calling " + func . __name__ ) return function_wrapper @our_decorator def foo ( x ): print ( "Hi, foo has been called with " + str ( x )) foo ( "Hi" )
OUTPUT:
Before calling foo Hi, foo has been called with Hi After calling foo
We can decorate every other role which takes one parameter with our decorator 'our_decorator'. We demonstrate this in the post-obit. We have slightly changed our function wrapper, so that we can come across the upshot of the function calls:
def our_decorator ( func ): def function_wrapper ( x ): print ( "Earlier calling " + func . __name__ ) res = func ( 10 ) print ( res ) print ( "Afterward calling " + func . __name__ ) return function_wrapper @our_decorator def succ ( n ): render north + 1 succ ( 10 )
OUTPUT:
Before calling succ xi Later calling succ
It is likewise possible to decorate tertiary party functions, e.g. functions we import from a module. We tin't use the Python syntax with the "at" sign in this instance:
from math import sin , cos def our_decorator ( func ): def function_wrapper ( x ): print ( "Before calling " + func . __name__ ) res = func ( x ) print ( res ) print ( "After calling " + func . __name__ ) return function_wrapper sin = our_decorator ( sin ) cos = our_decorator ( cos ) for f in [ sin , cos ]: f ( three.1415 )
OUTPUT:
Earlier calling sin 9.265358966049026e-05 After calling sin Before calling cos -0.9999999957076562 Afterwards calling cos
All in all, we tin can say that a decorator in Python is a callable Python object that is used to change a function, method or form definition. The original object, the i which is going to be modified, is passed to a decorator as an argument. The decorator returns a modified object, east.k. a modified role, which is bound to the name used in the definition.
The previous function_wrapper works merely for functions with exactly 1 parameter. We provide a generalized version of the function_wrapper, which accepts functions with capricious parameters in the following example:
from random import random , randint , choice def our_decorator ( func ): def function_wrapper ( * args , ** kwargs ): print ( "Before calling " + func . __name__ ) res = func ( * args , ** kwargs ) print ( res ) print ( "After calling " + func . __name__ ) return function_wrapper random = our_decorator ( random ) randint = our_decorator ( randint ) choice = our_decorator ( pick ) random () randint ( 3 , eight ) choice ([ 4 , 5 , 6 ])
OUTPUT:
Before calling random 0.3206237466802222 After calling random Before calling randint 6 Later calling randint Before calling pick 5 Afterward calling choice
Use Cases for Decorators
Checking Arguments with a Decorator
In our affiliate most recursive functions we introduced the factorial office. Nosotros wanted to keep the function as simple as possible and nosotros didn't want to obscure the underlying thought, so we didn't contain whatever argument checks. So, if somebody called our role with a negative argument or with a float statement, our function would become into an countless loop.
The following programme uses a decorator function to ensure that the argument passed to the part factorial is a positive integer:
def argument_test_natural_number ( f ): def helper ( ten ): if blazon ( x ) == int and x > 0 : render f ( x ) else : raise Exception ( "Argument is non an integer" ) return helper @argument_test_natural_number def factorial ( n ): if n == 1 : return i else : return n * factorial ( n - one ) for i in range ( one , 10 ): print ( i , factorial ( i )) print ( factorial ( - 1 ))
OUTPUT:
1 1 two two 3 6 4 24 5 120 6 720 seven 5040 8 40320 9 362880
Counting Office Calls with Decorators
The post-obit case uses a decorator to count the number of times a function has been called. To be precise, we can use this decorator solely for functions with exactly one parameter:
def call_counter ( func ): def helper ( x ): helper . calls += 1 return func ( 10 ) helper . calls = 0 return helper @call_counter def succ ( ten ): render x + 1 print ( succ . calls ) for i in range ( ten ): succ ( i ) print ( succ . calls )
OUTPUT:
Nosotros pointed out that nosotros can utilize our previous decorator only for functions, which accept exactly one parameter. Nosotros volition use the *args and **kwargs notation to write decorators which tin can cope with functions with an capricious number of positional and keyword parameters.
def call_counter ( func ): def helper ( * args , ** kwargs ): helper . calls += 1 return func ( * args , ** kwargs ) helper . calls = 0 return helper @call_counter def succ ( x ): render ten + i @call_counter def mul1 ( x , y = 1 ): return x * y + 1 impress ( succ . calls ) for i in range ( 10 ): succ ( i ) mul1 ( iii , 4 ) mul1 ( 4 ) mul1 ( y = iii , x = 2 ) impress ( succ . calls ) print ( mul1 . calls )
OUTPUT:
Decorators with Parameters
We define two decorators in the following lawmaking:
def evening_greeting ( func ): def function_wrapper ( 10 ): impress ( "Expert evening, " + func . __name__ + " returns:" ) return func ( x ) return function_wrapper def morning_greeting ( func ): def function_wrapper ( x ): print ( "Good morning, " + func . __name__ + " returns:" ) return func ( ten ) return function_wrapper @evening_greeting def foo ( ten ): print ( 42 ) foo ( "Hi" )
OUTPUT:
Expert evening, foo returns: 42
These two decorators are nearly the same, except for the greeting. We desire to add a parameter to the decorator to be capable of customizing the greeting, when we practise the ornamentation. Nosotros take to wrap another function around our previous decorator function to accomplish this. We can now easily say "Good Morning" in Greek:
def greeting ( expr ): def greeting_decorator ( func ): def function_wrapper ( x ): print ( expr + ", " + func . __name__ + " returns:" ) func ( 10 ) render function_wrapper return greeting_decorator @greeting ( "καλημερα" ) def foo ( x ): print ( 42 ) foo ( "Hi" )
OUTPUT:
καλημερα, foo returns: 42
If we don't want or cannot use the "at" decorator syntax, we can exercise it with role calls:
def greeting ( expr ): def greeting_decorator ( func ): def function_wrapper ( x ): print ( expr + ", " + func . __name__ + " returns:" ) return func ( x ) return function_wrapper return greeting_decorator def foo ( x ): print ( 42 ) greeting2 = greeting ( "καλημερα" ) foo = greeting2 ( foo ) foo ( "Hi" )
OUTPUT:
καλημερα, foo returns: 42
Of course, we don't need the additional definition of "greeting2". We tin can direct apply the result of the call "greeting("καλημερα")" on "foo":
foo = greeting("καλημερα")(foo)
The way we accept divers decorators so far hasn't taken into business relationship that the attributes
-
__name__
(proper noun of the part), -
__doc__
(the docstring) and -
__module__
(The module in which the role is defined)
of the original functions will exist lost after the decoration.
The following decorator will be saved in a file greeting_decorator.py:
def greeting(func): def function_wrapper(x): """ function_wrapper of greeting """ print("Hi, " + func.__name__ + " returns:") return func(x) return function_wrapper
We phone call it in the following program:
from greeting_decorator import greeting @greeting def f ( ten ): """ simply some silly part """ render 10 + four f ( x ) impress ( "part proper noun: " + f . __name__ ) print ( "docstring: " + f . __doc__ ) print ( "module name: " + f . __module__ )
OUTPUT:
How-do-you-do, f returns: function name: function_wrapper docstring: function_wrapper of greeting module name: greeting_decorator
We get the "unwanted" results above.
We tin can save the original attributes of the function f, if we assign them inside of the decorator. Nosotros modify our previous decorator appropriately and save it as greeting_decorator_manually.py:
def greeting(func): def function_wrapper(x): """ function_wrapper of greeting """ impress("How-do-you-do, " + func.__name__ + " returns:") render func(x) function_wrapper.__name__ = func.__name__ function_wrapper.__doc__ = func.__doc__ function_wrapper.__module__ = func.__module__ render function_wrapper
In our main program, all we have to do is alter the import statement.
from greeting_decorator_manually import greeting
Fortunately, we don't have to add all this code to our decorators to take these results. We can import the decorator "wraps" from functools instead and decorate our part in the decorator with it:
from functools import wraps def greeting ( func ): @wraps ( func ) def function_wrapper ( ten ): """ function_wrapper of greeting """ print ( "How-do-you-do, " + func . __name__ + " returns:" ) return func ( x ) render function_wrapper
Classes instead of Functions
The call method
So far we used functions every bit decorators. Before we tin can define a decorator equally a class, nosotros have to introduce the __call__
method of classes. We mentioned already that a decorator is simply a callable object that takes a function as an input parameter. A role is a callable object, simply lots of Python programmers don't know that in that location are other callable objects. A callable object is an object which tin be used and behaves like a office just might not be a function. Information technology is possible to ascertain classes in a manner that the instances will be callable objects. The __call__
method is called, if the instance is called "similar a function", i.e. using brackets.
class A : def __init__ ( cocky ): impress ( "An instance of A was initialized" ) def __call__ ( self , * args , ** kwargs ): print ( "Arguments are:" , args , kwargs ) x = A () print ( "now calling the case:" ) x ( three , iv , ten = 11 , y = 10 ) print ( "Let'due south telephone call it again:" ) x ( 3 , 4 , x = 11 , y = x )
OUTPUT:
An instance of A was initialized now calling the instance: Arguments are: (iii, 4) {'x': xi, 'y': ten} Let's call it once again: Arguments are: (3, iv) {'x': 11, 'y': 10}
We tin write a class for the fibonacci function by using the __call__
method:
grade Fibonacci : def __init__ ( self ): self . enshroud = {} def __call__ ( cocky , n ): if north non in self . cache : if n == 0 : self . cache [ 0 ] = 0 elif n == one : self . cache [ 1 ] = 1 else : self . enshroud [ northward ] = self . __call__ ( n - 1 ) + self . __call__ ( north - ii ) return cocky . cache [ n ] fib = Fibonacci () for i in range ( xv ): print ( fib ( i ), end = ", " )
OUTPUT:
0, 1, ane, 2, 3, 5, 8, thirteen, 21, 34, 55, 89, 144, 233, 377,
You tin discover further information on the __call__
method in the affiliate Magic Functions of our tutorial.
Using a Form equally a Decorator
We will rewrite the following decorator every bit a grade:
def decorator1 ( f ): def helper (): print ( "Decorating" , f . __name__ ) f () return helper @decorator1 def foo (): print ( "inside foo()" ) foo ()
OUTPUT:
Decorating foo inside foo()
The following decorator implemented as a form does the same "job":
grade decorator2 : def __init__ ( self , f ): self . f = f def __call__ ( cocky ): print ( "Decorating" , self . f . __name__ ) self . f () @decorator2 def foo (): print ( "inside foo()" ) foo ()
OUTPUT:
Decorating foo inside foo()
Both versions return the aforementioned output.
Live Python training
Upcoming online Courses
Enrol hither
Source: https://python-course.eu/advanced-python/decorators-decoration.php
Posted by: andraderessivoisin1962.blogspot.com
0 Response to "How To Create A Decorator In Python"
Post a Comment