Python learning path-Day4, python path-day4
Python learning path: 4th days
Learning Content:
1. iterator and Generator
2. decorator
1. iterator and Generator
Iterator
Iteration is one of the most powerful functions of Python and a way to access collection elements.
An iterator is an object that can remember the traversal position.
The iterator object is accessed from the first element of the set until all elements are accessed. The iterator can only move forward and never backward.
The iterator has two basic methods:Iter ()AndNext ()
String, list, or tuples can be used to create an iterator.
One advantage of the iterator is that it does not require that all elements in the entire iteration process be prepared in advance. The iterator calculates an element only when it iterates to an element. Before or after this, the element may not exist or be destroyed. This feature makes it especially suitable for Traversing large or infinite sets, such as several G files.
Features:
>>> List = [1, 2, 3, 4] >>> it = iter (list) # create iterator object >>> print (next (it )) # output iterator next element 1 >>> print (next (it) 2
The iterator object can be traversed using the regular for statement:
List = [1, 2, 4] it = iter (list) # create an iterator object for x in it: print (x)
Run the above program and the output result is as follows:
1234
You can also use the next () function:
Import sys # introduce sys module list = [1, 3, 4] it = iter (list) # create iterator object while True: try: print (next (it) iterator t StopIteration: sys. exit ()
Run the above program and the output result is as follows:
1234
All the data structures and objects that can be traversed through the for loop can be called iteratable, that is, Iterable. We now have access to Iterable data structures including list, tuple, dict, string, set, and generator (as described below ).
But what are the differences between them? Generator can not only use the for loop, but alsonext()The function continues to call and returns the next value until it returnsStopIterationError.
Can benext()The object that calls the function and returns the next value is called the Iterator.. Generator is an Iterable and Iterator, while list, tuple, str, and dict are not Iterator.
Of course, you can useisinstance()Determines whether an object is an Iterator.
>>> from collections import Iterator>>> isinstance((x*x for x in range(10)), Iterator)True>>> isinstance([],Iterator)False>>> isinstance({},Iterator)False>>> isinstance("ABC",Iterator)False
If you want to convert Iterable objects such as list, tuple, str, and dict into Iterator, you can use the iter () function to change the Iterable object to Iterator.
>>> isinstance(iter("ABC"),Iterator)True>>> i = iter("ABC")>>> next(i)'A'>>> next(i)'B'>>> next(i)'C'
What is the difference between Iterable and Iterator objects?
In python, The Iterator object represents a data stream. The Iterator object can be called by the next () function to return the next value until all the data is returned. The Iterator object data stream can be considered as a sequence, but the length of the sequence is unpredictable, because the next data can only be called through the next () or for loop as needed, therefore, the calculation of Iterator objects is a kind of inert computing.
Because of this feature, Iterator can represent an infinitely large data stream, such as all natural numbers, and list cannot complete such a task.
List Generator
Before talking about the generator, let's talk about the list generator.
The list generator, as its name implies, is used to generate a list. Previously, when we generated a list, we usually first defined a list = [], and then used the for loop to append the elements to the list one by one.
To generate a simple list, you can use list (range (1,100). What if you need to generate [1*2, 3*3?
>>> [x*x for x in range(10)][0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
In addition, you can add more elements to determine that x meets the requirements is used for computing.
>>> [x*x for x in range(10) if x%2==0][0, 4, 16, 36, 64]
Multiple loops can also generate a full arrangement.
>>> [x+y for x in 'ABC' for y in 'LMN']['AL', 'AM', 'AN', 'BL', 'BM', 'BN', 'CL', 'CM', 'CN']>>>>>> [x+y+z for x in 'ABC' for y in 'LMN' for z in 'OPQ' if z=='Q']['ALQ', 'AMQ', 'ANQ', 'BLQ', 'BMQ', 'BNQ', 'CLQ', 'CMQ', 'CNQ']
Generator
Through the list generator, we can create a list. However, due to the memory capacity limit, the list capacity must be limited. If we only need to use a part of the list, we do not need to generate a large list at once, directly generate as needed, and the list generator provides this function.
The generator is equivalent to an algorithm used to generate list elements. With this algorithm, you don't need to generate many elements at once. You only need to generate the elements as needed, saving a lot of space. In Python, this one-side loop computing mechanism is called generator.
To create a generator, you only need to change [] of the list generator.
>>> [x*x for x in range(10)][0, 1, 4, 9, 16, 25, 36, 49, 64, 81]>>> (x*x for x in range(10))<generator object <genexpr> at 0x00000259A50BD308>
Obtain the elements in generator. You can use the next () function to generate the next element of generator.
>>> g=(x*x for x in range(10))>>> next(g)0>>> next(g)1>>> next(g)4...>>> next(g)81>>> next(g)Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration
By calling the next () function, you can traverse all the elements of the generator until the StopIteration error is thrown.
However, the most commonly used is the for loop. After all, generator is also an Iterator object.
>>> g=(x*x for x in range(5))>>> for element in g:... print(element)...014916
Generally, after we create a generator, we basically do not call next (), but iterate it through the for loop, and do not need to care about the StopIteration error.
Sometimes, the algorithm for calculating elements is very complex and cannot be implemented through the list generator. Instead, the algorithm needs to be printed out through functions, such as the Fibonacci tangent sequence.
#! /Usr/bin/env pythondef fib (Max): n = 0 a, B = 0, while n <Max: print (B) a, B = B, result of a + B n = n + 1fib (5): 11235
In Python, yield functions are called generators ).
Unlike normal functions, a generator is a function that returns an iterator and can only be used for iterative operations. To put it simply, a generator is an iterator.
When the generator is called to run, the function will pause and save all current lamp running information every time yield is encountered, and the yield value will be returned. The next execution of the next () method continues from the current position.
#! /Usr/bin/env pythondef fib (Max): n = 0 a, B = 0, while n <Max: yield B a, B = B, a + B n = n + 1g = fib (5) print (next (g) for I in g: print (I) Result: 11235
During the execution of the function, return is returned, but the function becomes a generator function. If yield is encountered, the function is interrupted and the yield parameter is returned.
If yield is encountered, it will interrupt and return. Can we pass some parameters through yield?
#! /Usr/bin/env pythondef fib (Max): n = 0 a, B = 0, while n <Max: e = yield B print (e) a, B = B, a + B n = n + 1g = fib (5) print (next (g) print (g. send ("the third element") print (g. send ("forth") Result: 1None1the third element2forth3
In the above generator definition, the e = yield B statement is used. In this way, when yield returns B, it can also pass g. send (arg) to input a parameter assigned to e, so that the parameter can be passed through generator.
>>> def fib(Max):... n = 0... a,b = 0,1... while n < Max:... e = yield b... print(e)... a,b = b,a+b... n = n + 1...>>> g = fib(5)>>> g.send("the first element")Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: can't send non-None value to a just-started generator>>> g.send(None)1
Note: Why does the above Code report an error? It indicates that when a generator is started, only None can be input as the parameter, while generator is equivalent to next (g )!
2. decorator
Principles:
1. the source code of the decorated function cannot be modified.
2. The call method of the decorated function cannot be modified.
3. The execution result of the decorated function cannot be changed.
The decorator is transparent to the decorated function.
How to Understand the decorator
1. A function is a variable"
2. High-Order Functions
A: pass a function name as a real parameter to another function.
B: The returned value contains the function name.
3. nested Functions
Declaring another function in a function is called function nesting.
Reference: http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318435599930270c0381a3b44db991cd6d858064ac0000
Decorator is an advanced Python syntax. The decorator Can process a function, method, or class.
A function is also an object, and a function object can be assigned to a variable. Therefore, a function can also be called through a variable.
>>> def now():... print('2017-05-01')...>>> f = now>>> f()2017-05-01
The function object has a _ name _ attribute. You can get the function name:
>>> now.__name__'now'>>> f.__name__'now'
Now, let's assume that we want to enhance the function of the now () function. For example, logs are automatically printed before and after a function call, but you do not want to modify the definition of the now () function. This method of dynamically adding a function during code running is called: decorator ).
In essence, decorator is a high-level function that returns a function. Therefore, we need to define a decorator that can print logs, which can be defined as follows:
def log(func): def wrapper(*args, **kwargs): print("call %s():" % func.__name__) return func(*args, **kwargs) return wrapper
Observe the preceding log. Because it is a decorator, a function is accepted as a parameter and a function is returned. We need to use the @ Syntax of Python to place decorator in the definition of the function:
@ Log # Put this sentence here, it is equivalent to executing now = log (now) def now (): print ")
Calling the now () function not only runs the now () function itself, but also prints a line of log before running the now () function:
>>> now()call now():2017-05-01
Placing @ log in the definition of the now () function is equivalent to executing the statement:
now = log( now )
Because log () is a decorator and returns a function, the original now () function still exists, but now the now variable with the same name points to the new function, so now () is called () the new function will be executed, that is, the wrapper () function returned in the log () function.
If the decorator itself needs to input parameters, you need to write a high-order function that returns the decorator, which is more complex to write. For example, to customize the log text:
>>> def log(text):... def decorator(func):... def wrapper(*args, **kwargs):... print("%s %s():" % (text, func.__name__))... return func(*args, **kwargs)... return wrapper... return decorator...>>> @log("execute")... def now():... print("2017-05-01")...>>> now()execute now():2017-05-01
Compared with the two-layer nested decorator, the effect of layer-3 Nesting is as follows:
>>> now = log("execute")(now)
Let's analyze the preceding statement. First, execute log ('execute '). The returned result is the decorator function. when calling the returned function, the parameter is the now function, and the returned value is the wrapper function.
The definition of the above two types of decorator is correct, but the last step is poor. Because the function is also an object. It has the attributes such as _ name _, but you can see the function after decorator decoration. Their _ name _ has changed from the original 'now 'to 'wrapper ':
>>> now.__name__'wrapper'
Because the returned wrapper () function name is 'wrapper', You need to copy the _ name _ attributes of the original function to the wrapper () function. Otherwise, some code execution that depends on the function signature will fail.
No need to write wrapper. _ name _ = func. python built-in functools. wraps does this. Therefore, a complete decorator statement is as follows:
import functoolsdef log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper@log("execute")def now(): print("2017-05-01")now()
Or for the decorator with parameters:
import functoolsdef log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print("%s %s():" % (text, func.__name__)) return func(*args, **kwargs) return wrapper return decorator@log("execute")def now(): print("2017-05-01")now()
Decorator summary:
In the object-oriented (OOP) design pattern, decorator is called the decoration pattern. The decoration mode of OOP needs to be implemented through inheritance and combination. In addition to the decorator that supports OOP, Python directly supports decorator at the syntax level (learned after object-oriented ). Python decorator can be implemented using functions or classes.
Decorator can enhance functions of functions. Although it is a bit complicated to define, It is very flexible and convenient to use.