Decorator usage in Python and python
This article describes how to use the decorator in Python. Share it with you for your reference. The specific analysis is as follows:
This is caused by a problem in stackoverflow first. If the following code is used:
Copy codeThe Code is as follows: @ makebold
@ Makeitalic
Def say ():
Return "Hello"
Print the following output:
<B> <I> Hello <I> </B>
What do you do? The answer is:
Copy codeThe Code is as follows: def makebold (fn ):
Def wrapped ():
Return "<B>" + fn () + "</B>"
Return wrapped
Def makeitalic (fn ):
Def wrapped ():
Return "<I>" + fn () + "</I>"
Return wrapped
@ Makebold
@ Makeitalic
Def hello ():
Return "hello world"
Print hello () # Return <B> <I> hello world </I> </B>
Now let's take a look at how to understand Python decorators in some basic ways.
The decorator is a well-known design model and is often used in scenarios with cut-plane requirements. It is more classic in terms of log insertion, performance testing, and transaction processing. The decorator is an excellent design for solving such problems. With the decorator, we can extract a large number of identical codes irrelevant to the function itself and continue to reuse them. In summary, the purpose of the decorator is to add additional functions to existing objects.
1.1. How does the demand come from?
The definition of the decorator is very abstract. Let's look at a small example.
Copy codeThe Code is as follows: def foo ():
Print 'in foo ()'
Foo ()
This is a boring function. But suddenly there was a more boring person. We called him B Jun and said, "I want to see how long it took to execute this function. Well, we can do this:
Copy codeThe Code is as follows: import time
Def foo ():
Start = time. clock ()
Print 'in foo ()'
End = time. clock ()
Print 'used: ', end-start
Foo ()
Very good, functionality looks impeccable. But B suddenly didn't want to look at this function at the moment. He became more interested in another function named foo2.
What should we do? If you copy the newly added code to foo2, this makes a big mistake ~ Isn't copying anything the most annoying! What if B continues to read other functions?
1.2. The token should be retained as it remains unchanged.
Remember, if a function is a first-class citizen in Python, we can consider redefining a function timeit, passing the reference of foo to it, and then calling foo in timeit for timing, in this way, we have achieved the goal of not modifying the definition of foo, and no matter how many functions B has read, we don't have to modify the definition of the function!
Copy codeThe Code is as follows: import time
Def foo ():
Print 'in foo ()'
Def timeit (func ):
Start = time. clock ()
Func ()
End = time. clock ()
Print 'used: ', end-start
Timeit (foo)
It seems that there is no logic problem. Everything is beautiful and works properly !...... Wait, we seem to have modified the code for calling. Originally we called: foo () in this way and changed it to: timeit (foo ). In this case, if foo is called at N, You have to modify the code at N. Or even more extreme, consider the situation where the code called somewhere cannot be modified. For example, this function is used by someone else.
1.3. minimize changes!
In this case, let's try to avoid modifying the called code. If you do not modify the called code, it means that calling foo () requires the effect of calling timeit (foo. We can think of assigning timeit to foo, but timeit seems to have a parameter ...... Try to unify the parameters! If timeit (foo) does not directly produce calling results, but returns a function consistent with the foo parameter list ...... It is easy to do. Assign the return value of timeit (foo) to foo. Then, the code that calls foo () does not need to be modified!
Copy codeThe Code is as follows: #-*-coding: UTF-8 -*-
Import time
Def foo ():
Print 'in foo ()'
# Define a timer, input one, and return another method with the timer function attached
Def timeit (func ):
# Define an embedded packaging function to pack the input function with the timing Function
Def wrapper ():
Start = time. clock ()
Func ()
End = time. clock ()
Print 'used: ', end-start
# Return the packaged Function
Return wrapper
Foo = timeit (foo)
Foo ()
In this way, a simple timer is ready! We only need to add foo = timeit (foo) before calling foo after defining foo to achieve the purpose of timing. This is the concept of the decorator, it looks like foo is decorated by timeit. In this example, the function requires timing when entering and exiting, which is called an Aspect. This Programming method is called Aspect-Oriented Programming ). Compared with the traditional top-down execution method, it seems that a logic is inserted horizontally in the function execution process. In a specific business area, a large amount of repeated code can be reduced. There are quite a few terms for Aspect-oriented programming. I will not introduce them more here. If you are interested, you can look for relevant materials.
This example is only used for demonstration and does not take into account the situation that foo has parameters and has returned values. The task of perfecting it is handed over to you :)
The above Code seems to be no longer streamlined, and Python provides a syntax sugar to reduce the input of characters.
Copy codeThe Code is as follows: import time
Def timeit (func ):
Def wrapper ():
Start = time. clock ()
Func ()
End = time. clock ()
Print 'used: ', end-start
Return wrapper
@ Timeit
Def foo ():
Print 'in foo ()'
Foo ()
Focus on the @ timeit of the 11th rows. Adding this line in the definition is equivalent to writing foo = timeit (foo). Never think that @ has another magic power. In addition to fewer character input, there is an additional benefit: It looks more decorative.
To understand python decorators, we must first understand that functions in Python are also considered objects. This is important. Let's take a look at an example:
Copy codeThe Code is as follows: def shout (word = "yes "):
Return word. capitalize () + "! "
Print shout ()
# Output: 'Yes! '
# As an object, you can assign a function to any other object variable.
Scream = shout
# Note that we do not use parentheses because we are not calling a function.
# Assign the shout function to scream, that is, you can call shout through scream.
Print scream ()
# Output: 'Yes! '
# Also, you can delete the old name shout, but you can still access this function through scream
Del shout
Try:
Print shout ()
Failed t NameError, e:
Print e
# Output: "name 'Shout 'is not defined"
Print scream ()
# Output: 'Yes! '
Now let's talk about this topic. Let's first look at another interesting attribute of python: the function can be defined in the function:
Copy codeThe Code is as follows: def talk ():
# You can define another function in talk.
Def whisper (word = "yes "):
Return word. lower () + "...";
#... And use it immediately
Print whisper ()
# Every time you call 'tal', the whisper defined in the talk will also be called.
Talk ()
# Output:
# Yes...
# However, "whisper" does not exist independently:
Try:
Print whisper ()
Failed t NameError, e:
Print e
# Output: "name 'whisper 'is not defined "*
Function reference
From the above two examples, we can conclude that, since a function is an object:
1. It can be assigned to other variables
2. It can be defined in another function.
This means that the function can return a function. See the following example:
Copy codeThe Code is as follows: def getTalk (type = "shout "):
# Define another function
Def shout (word = "yes "):
Return word. capitalize () + "! "
Def whisper (word = "yes "):
Return word. lower () + "...";
# Then we return one of them
If type = "shout ":
# We didn't use () because we are not calling this function
# We are returning this function
Return shout
Else:
Return whisper
# How can I use it?
# Assign this function to a variable
Talk = getTalk ()
# Here you can see that talk is actually a function object:
Print talk
# Output: <function shout at 0xb7ea817c>
# One of the objects returned by the function:
Print talk ()
# You can call the following directly:
Print getTalk ("whisper ")()
# Output: yes...
Also, since a function can be returned, we can pass it as a parameter to the function:
Copy codeThe Code is as follows: def doSomethingBefore (func ):
Print "I do something before then I call the function you gave me"
Print func ()
DoSomethingBefore (scream)
# Output:
# I do something before then I call the function you gave me
# Yes!
Here you can understand the decorator, and others can be considered as the package. That is to say, it allows you to execute code before and after the decoration without changing the function content.
Hand Decoration
So how do I perform manual decoration?
Copy codeThe Code is as follows: # The decorator is a function, and its parameter is another function.
Def my_shiny_new_decorator (a_function_to_decorate ):
# Another function is defined internally: An inner.
# This function encapsulates the original function, so you can execute some code before or after it
Def the_wrapper_around_the_original_function ():
# Put some code that you want before real function execution
Print "Before the function runs"
# Execute the original function
A_function_to_decorate ()
# Put some code that you want to execute the original function
Print "After the function runs"
# At this moment, "a_function_to_decrorate" has not been executed. We have returned the created encapsulated function.
# The package contains functions and the code executed before and after the function. The package is ready.
Return the_wrapper_around_the_original_function
# As you can imagine, you have created a function that you will never touch again.
Def a_stand_alone_function ():
Print "I am a stand alone function, don't you dare modify me"
A_stand_alone_function ()
# Output: I am a stand alone function, don't you dare modify me
# Well, you can encapsulate it to implement behavior extension. It can be simply thrown to the decorator.
# The decorator dynamically encapsulates it with the code you want and returns a new available function.
A_stand_alone_function_decorated = my_shiny_new_decorator (a_stand_alone_function)
A_stand_alone_function_decorated ()
# Output:
# Before the function runs
# I am a stand alone function, don't you dare modify me
# After the function runs
Now you may require that every time you call a_stand_alone_function, the actual call is a_stand_alone_function_decorated. The implementation is also very simple. You can use my_shiny_new_decorator to assign a value to a_stand_alone_function.
Copy codeThe Code is as follows: a_stand_alone_function = my_shiny_new_decorator (a_stand_alone_function)
A_stand_alone_function ()
# Output:
# Before the function runs
# I am a stand alone function, don't you dare modify me
# After the function runs
# And guess what, that's EXACTLY what decorators do!
Decorator secrets
In the preceding example, we can use the decorator Syntax:
Copy codeThe Code is as follows: @ my_shiny_new_decorator
Def another_stand_alone_function ():
Print "Leave me alone"
Another_stand_alone_function ()
# Output:
# Before the function runs
# Leave me alone
# After the function runs
You can also accumulate Decoration:
Copy codeThe Code is as follows: def bread (func ):
Def wrapper ():
Print "</'''>"
Func ()
Print "<\ ______/>"
Return wrapper
Def ingredients (func ):
Def wrapper ():
Print "# tomatoes #"
Func ()
Print "~ Salad ~ "
Return wrapper
Def sandwich (food = "-- ham --"):
Print food
Sandwich ()
# Output: -- ham --
Sandwich = bread (ingredients (sandwich ))
Sandwich ()
# Outputs:
# </''' \>
# Tomatoes #
# -- Ham --
#~ Salad ~
# <\______/>
Use the python modifier Syntax:
Copy codeThe Code is as follows: @ bread
@ Ingredients
Def sandwich (food = "-- ham --"):
Print food
Sandwich ()
# Output:
# </''' \>
# Tomatoes #
# -- Ham --
#~ Salad ~
# <\______/>
The order of the decorator is very important.:
Copy codeThe Code is as follows: @ ingredients
@ Bread
Def strange_sandwich (food = "-- ham --"):
Print food
Strange_sandwich ()
# Output:
# Tomatoes #
# </''' \>
# -- Ham --
# <\______/>
#~ Salad ~
Finally, answer the questions mentioned above:
Copy codeThe Code is as follows: # makebold is used to convert the modifier to bold.
Def makebold (fn ):
# Return this function
Def wrapper ():
# Insert code before and after execution
Return "<B>" + fn () + "</B>"
Return wrapper
# The decorator makeitalic is used to convert it to italic.
Def makeitalic (fn ):
# Return this function
Def wrapper ():
# Insert code before and after execution
Return "<I>" + fn () + "</I>"
Return wrapper
@ Makebold
@ Makeitalic
Def say ():
Return "hello"
Print say ()
# Output: <B> <I> hello </I> </B>
# Equivalent
Def say ():
Return "hello"
Say = makebold (makeitalic (say ))
Print say ()
# Output: <B> <I> hello </I> </B>
Built-in decorator
There are three built-in decorators: staticmethod, classmethod, and property. They are used to convert the instance methods defined in the class into static methods, class methods, and class attributes. Since functions can be defined in a module, static methods and class methods are not very useful unless you want to fully object-oriented programming. Attributes are not indispensable, and Java is also very moist without attributes. From my own Python experience, I have never used property, and the frequency of using staticmethod and classmethod is also very low.
Copy codeThe Code is as follows: class Rabbit (object ):
Def _ init _ (self, name ):
Self. _ name = name
@ Staticmethod
Def newRabbit (name ):
Return Rabbit (name)
@ Classmethod
Def newRabbit2 (cls ):
Return Rabbit ('')
@ Property
Def name (self ):
Return self. _ name
The attribute defined here is a read-only attribute. If you need to be writable, You need to define another setter:
Copy codeThe Code is as follows: @ name. setter
Def name (self, name ):
Self. _ name = name
Functools Module
The functools module provides two decorators. This module is added after Python 2.5. Generally, most users use a version later than this version. But my usual work environment is 2.4 T-T
2.3.1. wraps (wrapped [, assigned] [, updated]):
This is a very useful ornament. A friend who has read the previous reflection article should know that a function has several special attributes, such as the function name. After being decorated, the function name foo in the previous example will become the wrapper name of the packaging function, if you want to use reflection, unexpected results may occur. The decorator can solve this problem and retain the special attributes of the decorated functions.
Copy codeThe Code is as follows: import time
Import functools
Def timeit (func ):
@ Functools. wraps (func)
Def wrapper ():
Start = time. clock ()
Func ()
End = time. clock ()
Print 'used: ', end-start
Return wrapper
@ Timeit
Def foo ():
Print 'in foo ()'
Foo ()
Print foo. _ name __
First, pay attention to the row 5th. If you comment out this line, foo. _ name _ will be 'wrapper '. In addition, I believe you have noticed that this decorator has a parameter. In fact, there are two other optional parameters. Attribute names in assigned are replaced by values, while attribute names in updated are merged by update, you can obtain the default values by viewing the source code of functools. For this decorator, it is equivalent to wrapper = functools. wraps (func) (wrapper ).
2.3.2. total_ordering (cls ):
This decorator is useful in specific scenarios, but it is added after Python 2.7. It serves to add other comparison methods to the classes that implement at least _ lt _, _ le _, _ gt _, and _ ge _., this is a class decorator. If you do not understand the source code, take a closer look at the source code of the decorator:
Copy codeThe Code is as follows: def total_ordering (cls ):
"Class decorator that fills in missing ordering methods """
Convert = {
'_ Lt _': [('_ gt _', lambda self, other: other <self ),
('_ Le _', lambda self, other: not other <self ),
('_ Ge _', lambda self, other: not self <other)],
'_ Le _': [('_ ge _', lambda self, other: other <= self ),
('_ Lt _', lambda self, other: not other <= self ),
('_ Gt _', lambda self, other: not self <= other)],
'_ Gt _': [('_ lt _', lambda self, other: other> self ),
('_ Ge _', lambda self, other: not other> self ),
('_ Le _', lambda self, other: not self> other)],
'_ Ge _': [('_ le _', lambda self, other: other> = self ),
('_ Gt _', lambda self, other: not other> = self ),
('_ Lt _', lambda self, other: not self> = other)]
}
Roots = set (dir (cls) & set (convert)
If not roots:
Raise ValueError ('must define at least one ordering operation: <><=> = ')
Root = max (roots) # prefer _ lt _ to _ le _ to _ gt _ to _ ge __
For opname, opfunc in convert [root]:
If opname not in roots:
Opfunc. _ name _ = opname
Opfunc. _ doc _ = getattr (int, opname). _ doc __
Setattr (cls, opname, opfunc)
Return cls
I hope this article will help you with Python programming.