simulate zip and map with iterative tools
======================================================================
We already know how zip can be combined to iterate over objects and how map maps functions.
>>> S1 = ' abc ' >>> S2 = ' xyz123 ' >>> list (Zip (S1,S2)) [(' A ', ' X '), (' B ', ' Y '), (' C ', ' Z ')]>> > list (Zip ([ -2,-1,0,1,2])) [( -2,), ( -1,), (0,), (1,), (2,)]>>> list (map (ABS, ( -2,-1,0,1,2))) [2, 1, 0, 1, 2] >>> list (map (pow,[1,2,3],[2,3,4,5)) [1, 8, 81]
---------------------------------------------------------------------------------------------------------------
You can write your own map (func,...) below. The
>>> def mymap (func,*seqs): res = []for args in Zip (*seqs): Res.append (func (*args)) return res>>> print ( Mymap (ABS, ( -2,-1,0,1,2))) [2, 1, 0, 1, 2]>>> print (Mymap (pow,[1,2,3],[2,3,4,5])) [1, 8, 81]
This version number is dependent on the special *args parameter pass syntax. It has multiple sequence parameters, which are unpacked as a zip reference so that it can be combined, and then the paired zip result is unpacked as a parameter for passing in the function.
That is, we are using the fact that zip is a major nesting operation in the map.
"Be sure to master the advanced techniques of writing such functions, the handling of the parameters!" 】
However. In fact, the previous version number shows the list parsing mode, which builds a list of the operation results in a for loop. So, this function can also be streamlined:
>>> def mymap (func,*seqs): Return [func (*args) for args in Zip (*seqs)]>>> print (Mymap (ABS, ( -2,- 1,0,1,2))) [2, 1, 0, 1, 2]>>> print (Mymap (pow,[1,2,3],[2,3,4,5])) [1, 8, 81]
When this code executes, the result is the same as before, but. This code is more refined and likely to execute faster.
It's just that. These two version numbers are a one-time build results list, which can be a waste of memory for larger lists. Now that you know the generator functions and expressions, it's easy to encode these two alternatives again to produce results on demand:
>>> def mymap (func,*seqs): For args in Zip (*seqs): Yield func (*args) >>> def mymap (func,*seqs): Return ( Func (*args) for args in Zip (*seqs)) >>> print (List (Mymap (pow,[1,2,3],[2,3,4,5))) [1, 8, 81]
When this code executes, the result is the same as in the previous section. However, this code is more refined and likely to execute faster.
It's just that. These two version numbers are a one-time build results list, which can be a waste of memory for larger lists. Now that you know the generator functions and expressions. Coding these two alternatives again to produce results on demand is very easy:
>>> def mymap (func,*seqs): For args in Zip (*seqs): Yield func (*args) >>> def mymap (func,*seqs): Return ( Func (*args) for args in Zip (*seqs)) >>> print (List (Mymap (pow,[1,2,3],[2,3,4,5))) [1, 8, 81]
The version number of the generator produces the same result, but returns the generator designed to support the iteration protocol.
The first version number yields one result per yield. The second version number returns the result of a generator expression doing the same thing.
----------------------------------------------------------------- ----------------------------------------------
Write your own zip (...)
The magic in the preceding examples is that they use the zip built-in function to pair the parameters from multiple sequences.
Following. We also come to simulate the built-in zip.
>>> def myzip (*seqs): Seqs = [List (s) for s in seqs]res = []while All (SEQS): Res.append (Tuple (s.pop (0) for s in SEQ s)) return res>>> S1,S2 = ' abc ', ' Xyz123 ' >>> print (Myzip (S1,S2)) [(' A ', ' X '), (' B ', ' Y '), (' C ', ' Z ')]
Note that all of the built-in functions here are used, assuming that all elements in an iterator object are true (or equivalent to non-null), it returns true. This built-in function is used to stop loops when the list has a parameter that becomes empty after the deletion.
However, the same as before. Since our zip builds and returns the list. Save memory by converting them to generators with yield so that each of them is one item in the result of each return.
>>> def myzip (*seqs): Seqs = [List (s) for s in Seqs]while All (SEQS): Yield tuple (s.pop (0) for S in Seqs) >>> ; List ([Myzip], (' A ', ' B ', ' C ', ' d '))) [(1, ' a '), (2, ' B '), (3, ' C ')]
Of course, it is also possible to complete its work by calculating the minimum length of the reference, with a minimum length, very easy to write nested list parsing to traverse the index range of the parameters
>>> def myzip (*seqs): Minlen = min (len (s) for S in seqs) return [tuple (S[i] for s in seqs) for I in range (Minlen)]& Gt;>> Myzip ([a], (' A ', ' B ', ' C ', ' d ')) [(1, ' a '), (2, ' B '), (3, ' C ')]
>>> def myzip (*seqs): Minlen = min (len (s) for S in seqs) for I in Range (Minlen): Yield tuple (s[i] for s in seqs) > >> list ([Myzip], (' A ', ' B ', ' C ', ' d '))) [(1, ' a '), (2, ' B '), (3, ' C ')]
The first example here returns a list. The second example uses the generator function. But in fact, it is also possible to use generator expressions, rather refining:
>>> def myzip (*seqs): Minlen = min (len (s) for S-seqs) return (tuple (s[i] for S-seqs) for I in Range (Minlen)) & Gt;>> Myzip ([a], (' A ', ' B ', ' C ', ' d ')) <generator object <genexpr> at 0x02bf1b98>>>> list ( Myzip ([[+], (' A ', ' B ', ' C ', ' d '))) [(1, ' a '), (2, ' B '), (3, ' C ')]
======================================================================
Timing the various methods of the iteration
List parsing has a performance advantage over the speed of a For loop statement, and map shows better or worse performance depending on the method being called. The generator expression looks more slowly than the list resolution. But they have reduced memory requirements to a minimum.
----------------------------------------------------------------- ----------------------------------------------
Timing the module
Python is very easy to time code. To see how the iteration options stack up, let's start with a simple but generic timer tool function that writes a module file so that it can be used in a variety of programs.
#File Mytimer.pyimport timereps = 1000repslist = Range (reps) def timer (Func,*pargs,**kargs): start = Time.clock () For i in repslist: ret = func (*pargs,**kargs) elapsed = Time.clock ()-Start return (Elapsed,ret)
This module takes the time to start, the number of times the function is called, and the start time minus the stop time. This makes it time to call arbitrary functions regardless of location and keyword parameters.
Note the following points:
(1) The time module of Python agrees to access the current hour. Accuracy varies with each platform.
On Windows, this call is known to achieve subtle precision. has been quite accurate.
(2) The range call is placed outside the timing loop due to the. The cost of its construction is not calculated in the Python2.6 timer function.
The range in Python3.0 is an iterator. Therefore, this step is not required.
(3) The reps count is a global variable, assuming that the importer can change it if needed: Mytimer.reps = N
When these are finished, the total time of the call is returned in a meta-Zuzhong, with the last return value of the function being timed so that the caller can verify its operation.
----------------------------------------------------------------- ----------------------------------------------
Timing Scripts
Now, to clock the speed of the iteration tool, execute a script like the following, which uses a timer module that has learned the relative speed of the various list building techniques.
# File Timeseqs.pyimport mytimer,sysreps = 10000repslist = Range (reps) def forloop (): res = [] for x in repslist:
res.append (ABS (x)) return resdef Listcomp (): return [ABS (x) for x in Repslist]def mapcall (): return list (Map (abs,repslist)) def genexpr (): return List (ABS (x) for x in repslist) def genfunc (): def gen (): For X in re PsList: yield abs (x) return list (Gen ()) print (sys.version) for test in (forloop,listcomp,mapcall,genexpr, Genfunc): elapsed,result = Mytimer.timer (test) print ('-' *33 ') print ('%-9s:%.5f = [%s...%s] '% ( TEST.__NAME__,ELAPSED,RESULT[0],RESULT[-1]))
This script tested five alternative ways to build a list of results. Also, each method runs 10 million levels of steps. Other words. Each of the five Tests builds a list of 10,000 elements 1000 times.
Note how the bottom code traverses one of the 4 function object's ancestors and prints out each __name__, which is a built-in property.
----------------------------------------------------------------- ----------------------------------------------
Timing Results
Test it on my Window10 computer. Output such as the following results--map is slightly faster than the list parsing. But both are much faster than for loops, and the generator expression and the build function are centered faster.
>>> 3.4.3 (v3.4.3:9b73f1c3e601, Feb, 22:43:06) [MSC v.1600 (Intel)]------------------------------ ---forloop : 1.20765 = [0...9999]---------------------------------listcomp:0.77999 = [0...9999]-------- -------------------------mapcall : 0.67569 = [0...9999]---------------------------------genexpr : 0. 91294 = [0...9999]---------------------------------genfunc : 0.87446 = [0...9999]
Suppose you change this script. Run a real operation (such as addition) on each iteration. Instead of calling ABS, a small built-in function, five functions are changed to:
Def forloop (): res = [] for x in repslist: res.append (x+10) return resdef Listcomp (): return [x+10 For x in Repslist]def mapcall (): return list (map (lambda x:x+10,repslist)) def genexpr (): return list (x+10 for x i n repslist) def genfunc (): def gen (): for x in repslist: yield x+10 return list (Gen ())
Look at the test results:
>>> 3.4.3 (v3.4.3:9b73f1c3e601, Feb, 22:43:06) [MSC v.1600 (Intel)]------------------------------ ---forloop : 1.12030 = [10...10009]---------------------------------listcomp:0.64926 = [10...10009]---- -----------------------------Mapcall : 1.35093 = [10...10009]---------------------------------genexpr : 0.79881 = [10...10009]---------------------------------genfunc : 0.85247 = [10...10009]
At this point, the call to map is a lambda function of its own definition, so it is slower than the for loop. But the list resolution is still the quickest.
Interpreter optimization is an issue with internalization. Performance analysis of Python code like this is a very technical thing to do. It's not really possible to predict which method will run better. and performance should not be the primary concern when we write Python code-to optimize Python code, first to write code for readability and simplicity. Then, if needed, then optimize.
=============================================================== =======
Problem:
1. What is the relationship between the generator and the iterator?
Generators are objects that support iterative protocols: They have __next__ methods. Advances repeatedly to the next element in the series results, as well as to the end of the series to throw exception events. In Python, we are able to create generator objects with Def, parenthesized List-parsed generator expressions, and class-defined special method __iter__ to write generator functions.
What does a 2.yield statement do?
When the yield statement is available, this statement allows Python to make a function-specific compilation of the genetic generator. When called. The generator object is returned and the iteration protocol is supported.
When the yield statement executes, the result is returned to the caller, letting the function's state hang. And then. When the caller calls the __next__ method again. This function will be able to continue execution once again after the last yield statement. The generator can also have a return statement. Used to terminate the generator.
python--iterators and parsing (3)