I often talk about Python decorators, iterators, and generators.
When learning python, the three major "namespaces" are a small difficulty for people without programming experience in other languages, this blog explains how to understand the decorator, iterator, and generator.
Why do I need a decorator?
What is a decoration device? "Decoration" literally refers to the act of beautifying the buildings according to certain ideas and styles. The so-called "ware" is a tool, for python, the decorator Can add new functions without modifying the original code. For example, after a software is launched, we need to add new functions without modifying the source code or calling the method. In python, we can use the decorator, we also need to consider the subsequent scalability when writing code. Let's take a look at the python decorator step by step.
A simple example introduces a non-parameter Modifier
Let's take a look at a few simple lines of code. The code runs for 2 seconds and then prints "hello boy! ":
Import timedef foo (): "print" time. sleep (2) print ("Hello boy! ") Foo ()
Now we need to add a program timing function for it, but we cannot modify the original code:
Import timedef timmer (func): def wrapper (): "" Timing function "time_start = time. time () func () time_end = time. time () print ("Run time is % f" % (time_end-time_start) return wrapperdef foo (): "" print "time. sleep (2) print ("Hello boy! ") Foo = timmer (foo) foo () # running result Hello boy! Run time is 2.000446
Look! We have implemented this function without modifying the original code. Because the function is also an object, we can pass the function foo as a parameter to the function timmer.
In python, there is a more concise way to replace foo = timmer (foo), using @ timmer, which is called syntax sugar in python.
Import timedef timmer (func): def wrapper (): "" Timing function "time_start = time. time () func () time_end = time. time () print ("Run time is % f" % (time_end-time_start) return wrapper @ timmer # equals foo = timmer (foo) def foo (): "print" "time. sleep (2) print ("Hello boy! ") Foo ()
Next we will analyze the execution process of the function step by step:
1. Import time module
import time
2. Define the timmer function. The defined function does not execute the code in the function.
def timmer(func):
3. Call the decorator, which is equivalent to foo = timer (foo). The function foo is passed to the timmer as a parameter.
@timmer
4. Run the timmer function and accept the parameter func = foo.
def timmer(func):
5. In the timmer function, the wrapper function is defined and the internal code of the wrapper function is not executed. Then, the wrapper function is returned as the return value.
return wrapper
6. Assign the return value to foo. in step 2, foo = timmer (foo). Remember it.
@ Timmer # equal to foo = timmer (foo)
7. run the function foo (), but the function here is no longer the original function. You can print foo, correct, because previously we passed wrapper as the return value to foo, so here foo is executing wrapper. To confirm this, you can print wrapper. They have the same memory address, so they all point to the same address space:
<Function timmer. <locals>. wrapper at 0x00000180E0A8A950> # print the foo result <function timmer. <locals>. wrapper at 0x000001F10AD8A950> # print the wrapper result foo ()
8. Run the function wrapper, record the start time, and execute the function func. in step 1, func is assigned a value by foo. Running func is to run the original function foo, sleep for 2 seconds, and print the string;
time_start=time.time() time.sleep(2) print("Hello boy!")
9. Record the end time, print the running time, and end the program.
Hello boy!Run time is 2.000161
Parameter decorations
In the previous example, the original function has no parameters. The following shows how to modify the decorator function when the original function has parameters?
Import timedef timmer (func): def wrapper (* args, ** kwargs): "" Timing function "start_time = time. time () res = func (* args, ** kwargs) end_time = time. time () print ("Run time is % f" % (end_time-start_time) return res return wrapper @ timmer def my_max (x, y ): "returns the maximum of two values" res = x if x> y else y time. sleep (2) return resres = my_max (1, 2) print (res) # Run the result Run time is 2.000175
When the original function needs to input parameters, in this example, my_max has two locations to form the input parameters. You only need to add two parameters to wrapper, in this example, variable parameters (* args, ** kwargs) are used. This parameter @ timmer equals to my_max (1, 2) = timmer (my_max)
Next let's look atDecorator with parameters:
Def auth (filetype): def au22 (func): def wrapper (* args, ** kwargs): if filetype = "file ": username = input ("Please input your username:") passwd = input ("Please input your password:") if passwd = '000000' and username = 'frank ': print ("Login successful") func () else: print ("login error! ") If filetype = 'SQL': print (" No SQL ") return wrapper return au2@auth (filetype = 'file ') # First, return an auste2 = "@ auste2 =" index = auste2 (index) = "index = wrapperdef index (): print (" Welcome to China ") index ()
If the decorator has parameters, an embedded function is required. Next we will analyze the execution process step by step:
1. Define the function auth
def auth(filetype):
2. To call the interpreter, first run the auth (filetype = 'file') function ')
@auth(filetype='file')
3. run the auth function and define a function au22. return the value as the return value. Then @ auth (filetype = 'file') is equivalent to @ au22. It is equivalent to index = au22)
def auth(filetype): def auth2(func): def wrapper(*args,**kwargs): return wrapper return auth2
4. Execute au2's (index), func = index, define the function wrapper, and return it. At this time, the index is actually equal to wrapper.
def wrapper(*args,**kwargs):return wrapper
5. when index is run, wrapper is run, and the internal code of the function is run, filetype = "file", the user is prompted to output the user name and password to determine whether the input is correct. If the input is correct, the execution function func () is equal to the execution of the original index, print
if filetype == "file": username=input("Please input your username:") passwd=input("Please input your password:") if passwd == '123456' and username == 'Frank': print("Login successful") func()
6. Test the running result
Please input your username:FrankPlease input your password:123456Login successfulWelcome to China
The decorator can also be superimposed:
Import time # def timmer (func): def wrapper (): "Timing function" time_start = time. time () func () time_end = time. time () print ("Run time is % f" % (time_end-time_start) # print ("---", wrapper) return wrapperdef auth (filetype): def au22 ): def wrapper (* args, ** kwargs): if filetype = "file": username = input ("Please input your username :") passwd = input ("Please input your password:") if passwd = '000000' and usern Ame = 'frank': print ("Login successful") func () else: print ("login error! ") If filetype = 'SQL': print (" No SQL ") return wrapper return au2@timmer @ auth (filetype = 'file ') # First, return an auste2 = "@ auste2 =" index = auste2 () = "index = wrapperdef index (): print (" Welcome to China ") index () # Test Result Please input your username: FrankPlease input your password: 123456 Login successfulWelcome to ChinaRun time is 7.966267
Annotation Optimization
Import timedef timmer (func): def wrapper (): "computing program running time" start_time = time. time () func () end_time = time. time () print ("Run time is % s:" % (end_time-start_time) return wrapper @ timmerdef my_index (): "print welcome" time. sleep (1) print ("Welcome to China! ") My_index () print (my_index. _ doc _) # running result Welcome to China! Run time is 1.0005640983581543: computing program running time
When the decorator is used, although the Code itself is not modified, the running of my_index is actually running wrapper, if we print the comments of my_index and wrapper (), how can we optimize it?
You can import wraps in the functools module. For details, see the following:
Import timefrom functools import wrapsdef timmer (func): @ wraps (func) def wrapper (): "computing program RunTime" start_time = time. time () func () end_time = time. time () print ("Run time is % s:" % (end_time-start_time) return wrapper @ timmerdef my_index (): "print welcome" time. sleep (1) print ("Welcome to China! ") My_index () print (my_index. _ doc _) # running result Welcome to China! Run time is 1.0003223419189453: Print welcome
As a result, the original function has not changed.
Why use an iterator?
Literally, iteration is an activity that repeats the feedback process. It is usually used to compare the desired goal or result. In python, it can be implemented using an iterator, first, describe the advantages and disadvantages of the iterator. If you do not understand it, you can skip it. After reading this blog, I believe you will understand what it means:
Advantages:
The iterator does not depend on the index when taking values, so that it can traverse objects without indexes, such as dictionaries and files.
Compared with the list, the iterator is inert computing, which saves more memory.
Disadvantages:
The length of the iterator cannot be obtained, and the list is not flexible.
Values can only be returned. values cannot be reversed.
What is an iterator?
So what is an iterator in python?
As long as the object has _ iter _ (), it can be iterated. The iterator can use the function next () to take values.
Let's look at a simple iterator:
My_list = [1, 2, 3] li = iter (my_list) # li = my_list. _ iter _ () print (li) print (next (li )) # Running result <list_iterator object at 0x000002591652C470> 2
You can see that using the built-in function iter, you can convert the list into a list iterator, use next () to get the value, get one value at a time, get the value after the value is finished, and then use next () exception StopIteration is reported, which can be avoided by exception handling. try-retry T-else is the most common exception handling structure:
My_list = [1, 2, 3] li = iter (my_list) while True: try: print (next (li) failed t StopIteration: print ("Over") break else: print ("get! ") # Run the result get! Get! Get! Over
View iteratable objects and iterator objects
The Iterable module can be used to determine whether an object can be iterated:
From collections import Iterables = "hello" # definition string l = [,] # definition list t = (, 3) # definition tuples d = {'A ': 1} # define the dictionary set1 = {1, 2, 3, 4} # define the set f = open ("a.txt") # define the text # check whether all data is iterative print (isinstance (s, iterable) print (isinstance (l, Iterable) print (isinstance (t, Iterable) print (isinstance (d, Iterable) print (isinstance (set1, Iterable )) print (isinstance (f, Iterable) # Run the result TrueTrueTrueTrueTrueTrue
Through judgment, we can determine that all common data types we know can be iterated.
The Iterator module can be used to determine whether an object is an Iterator:
From collections import Iterable, Iterators = "hello" l = [1, 2, 4] t = (1, 2, 3) d = {'A': 1} set1 = {1, 2, 3, 4} f = open ("a.txt") # check whether all print (isinstance (s, Iterator) print (isinstance (l, Iterator )) print (isinstance (t, Iterator) print (isinstance (d, Iterator) print (isinstance (set1, Iterator) print (isinstance (f, Iterator) # Run the result FalseFalseFalseFalseFalseTrue
We can see that only files are iterators, so you can directly use next () without converting them into iterators.
What is a generator?
The producer is a function with yield.
Let's look at a simple generator.
Def my_yield (): print ('first') yield 1g = my_yield () print (g) # running result <generator object my_yield at 0x00000241_d7e258>
The generator is also an iterator
From collections import Iteratordef my_yield (): print ('first') yield 1g = my_yield () print (isinstance (g, Iterator) # The running result is True.
Then you can use next () to set the value.
Print (next (g) # Run result first1
Generator Execution Process
Let's take a look at the following example to understand the production execution process.
Def my_yield (): print ('first') yield 1 print ('second') yield 2 print ('third') yield 3g = my_yield () next (g) next (g) # Run the result firstsecondThird
1. Define the generator my_yield and assign it to g
def my_yield():g=my_yield()
2. start executing next () for the first time, start executing the generator function, print the first statement, pause when yileld is encountered, and return a 1. If you want to print the return value, 1 is displayed.
print('first') yield 1
3. Execute the command twice and print the string (every execution will be paused)
print('second') yield 2 print('Third') yield 3
4. if you add next (), the StopIteration exception will be reported.
When the generator is paused, the function state is saved. For example:
Def foo (): I = 0 while True: yield I + = 1g = foo () for num in g: if num <10: print (num) else: break # running result
In a for loop, next () is hidden, paused once every next time, if statement is judged once, and then next time is executed. We can see that our while LOOP does not have an infinite loop, the status is saved.
Coroutine Functions
Let's take a look at the following generator and execution results.
Def eater (name): print ('% s start to eat food' % name) while True: food = yield print ('% s get % s, to start eat '% (name, food) print ('done') e = eater ('Frank') next (e) e. send ('egg') # send a value to yield and continue executing code e. send ('tomato') # Run the result Frank start to eat foodFrank get egg, to start eatFrank get tomato, to start eat
Send can directly transmit values to yield. functions that contain yield expressions are also called coroutine functions,
When running the program, you cannot directly send it. You must first use next () to initialize the generator.
If there are multiple such functions, we should go to next () each time we execute. To prevent this operation from being forgotten, we can use the decorator to initialize:
Def init (func): def wrapper (* args): res = func (* args) next (res) # Run next return res return wrapper @ initdef eater (name) here ): print ('% s start to eat food' % name) while True: food = yield print ('% s get % s, to start eat' % (name, food )) print ('done') e = eater ('frank') e. send ('egg') e. send ('tomato ')
Therefore, when there are more generators in the program that need to be initialized, you can directly call this modifier.
The Python decorators, iterators, and generators mentioned above are all the content shared by Alibaba Cloud xiaobian. I hope you can give us a reference and support for our guests.