Python Functional Programming Guide: a comprehensive explanation of the generator

Xiao Bian today to share a Python Functional Programming Guide for everyone: a comprehensive explanation of the generator, a good reference value, we want to help. Come and see, to follow the small series together
generator is an iterator, but also not just an iterator, but uses other than the iterator is really small, so we can proudly say: generator provides a very convenient self way iterator definition.

This is the last one functional programming guide, it seems that dragged on for a week before written, ah ......

  1. Generator (Generator)

1.1. Builder Introduction

First of all, please make sure that the generator is a kind of iterators. Builder has exactly the same behavior with the next method and iterators, which means that the generator can also be used Python's for loop. In addition, support for special syntax makes writing a generator of a conventional generator iterator is simpler than a lot of custom, one of the characteristics most commonly used generator so.

Starting Python 2.5, [PEP 342: Generator achieve synergies through enhanced program] achieved by adding more features to the generator, which means that the generator can also get more work done. In this section we will introduce later in the section.

1.2. Generator function

1.2.1. Use defined generator function generator

How to get a generator? First look at a snippet of code:

>>> def get_0_1_2():
... yield 0
... yield 1
... yield 2
...
>>> get_0_1_2
<function get_0_1_2 at 0x00B2CB70>

We define a function get_0_1_2, and you can see that this is indeed the type of function. But the difference is a normal function, get_0_1_2 function in vivo use keyword yield, which makes get_0_1_2 become a generator function. Characteristic generator function is as follows:

Generator function call returns a generator;

>>> generator = get_0_1_2()
>>> generator
<generator object get_0_1_2 at 0x00B1C7D8>

When the next method first call generator, the generator began to perform generator function (instead of building the generator when), suspended (suspended) until it hits yield, the yield parameters and will serve as the next method The return value;

>>> generator.next()
0

After each call to the next method generator, the generator from the last position to resume execution of the suspended generator function, pause until it encounters yield again and, as such, the parameters will yield as a return value next method;

>>> generator.next()
1
>>> generator.next()
2

Generator function end (the null return statement or reach the end of the function body) If, when calling the next method, this method will call the next raise StopIteration (ie for the termination condition for the loop);

>>> generator.next()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

Generator function each time suspended, all the variables of the function body will be archived (Freeze) in the producer, and the restored when execution resumes, and similar to the closure, even with a generator function returns generator, variable storage is also independent of each other.

Our small case and did not use variables, so here is another definition of a generator to demonstrate this feature:

>>> def fibonacci():
... a = b = 1
... yield a
... yield b
... while True:
... a, b = b, a+b
... yield b
...
>>> for num in fibonacci():
... if num > 100: break
... print num,
...
1 1 2 3 5 8 13 21 34 55 89

Do not be surprised to see while True, because the generator can be suspended, so the delay is calculated, an infinite loop does not matter. In this example we define the Fibonacci number sequence generator used to obtain a Fibonacci.

1.2.2. FAQ generator function

Next we discuss some interesting topics about the generator.

Your example in the generator function are no parameters, then the generator function can be parameterized it?

Of course, you can ah pro, and it supports all the functions of an argument. To known a generator function is also a function of :)

>>> def counter(start=0):
... while True:
... yield start
... start += 1
...

This is a start from the specified counter.

Since the generator function is also a function, it can use the return output return value it?

No pro, is such a generator function has a default return value - generator, you can no longer return value to one another; right, nor even a return None. But it may be an empty return statement ends. If you insist specify a return value for it, then Python syntax error exception will be presented in a defined position, like this:

>>> def i_wanna_return():
... yield None
... return None
...
 File "<stdin>", line 3
SyntaxError: 'return' with argument inside generator

Well, people need to secure the release of resources, you need to try ... finally in yield, it would be horse of God? (I just want to play you) I finally also yield once!

Python will then execute the code in the finally leaving the real try ... finally, and here regret to inform you, not to pause Oh! So the outcome you would have guessed it!

>>> def play_u():
... try:
... yield 1
... yield 2
... yield 3
... finally:
... yield 0
...
>>> for val in play_u(): print val,
...
1 2 3 0

* This is different in the case of return. The real return is to leave the block, so execution will finally clause when you return immediately.

* In addition, "in the block with a finally try clause yield" is defined in PEP 342, which means that only Python 2.5 or later this syntax is supported, in the following versions of Python 2.4 will get the exception syntax error.

If I need to access another generator in the generator's iterative process an iterative how to do? Written below so silly, very naive. .

>>> def sub_generator():
... yield 1
... yield 2
... for val in counter(10): yield val
...

Syntax improve this situation has been defined in [PEP 380: to entrust the sub-grammar generator], it is said be implemented in Python 3.3, there may also be fed back to the 2.x. After implementation, so you can write:

>>> def sub_generator():
... yield 1
... yield 2
... yield from counter(10)
 File "<stdin>", line 4
 yield from counter(10)
  ^
SyntaxError: invalid syntax

See grammatical errors wood? Now we are still a bit naive it ~

There are more questions? Please reply to this article :)

1.3. Coroutines (coroutine)

Coroutine (coroutine) generally refers to a function:

There are different from each other local variables, the instruction pointer, but still shared global variables;

You can easily suspend, resume, and a plurality of entry and exit points;

Synergy between multiple programs running performance of collaboration, such as the result of a process run A to B in the need to continue to perform.

Coroutine determine the characteristics of a moment, only a coordinated program is running (ignoring the case of multi-threaded). Thanks to this, coroutines can be passed directly between objects without regard to resource lock, or other direct wake-up process without the need for active co-sleep, like a built-in lock thread. In scenarios in line with the characteristics of coroutines, using the coroutine will undoubtedly be more convenient than using threads.

On the other hand, concurrent coroutines not in fact limit its application to the scene in a very narrow range, this feature makes the coroutine to be brought more compared with the conventional function, rather than thread. Of course, more complicated than many threads coroutine, and more powerful
Python 2.5 enhancements generator implements additional features of coroutines, in this version, the generator is added the following method:

Send (value):
Send next is another recovery method in addition to the generator. In Python 2.5, yield statements into a yield expression, which means you can now have a yield value, and this value is to be called upon to restore execution method parameters send send method call generator.

>>> def repeater():
... n = 0
... while True:
... n = (yield n)
...
>>> r = repeater()
>>> r.next()
0
>>> r.send(10)
10

* Send incoming calls before non-None value, the generator must be in a suspended state, otherwise it will throw an exception. However, it does not start the generator can still use None as calling the send.

* If you use the next recovery generator, the value of yield expression will be None.

close():

This method is used to close the generator. Call or send again to close next generation will raise StopIteration right.

throw(type, value=None, traceback=None):

This method for generating an internal (suspended at a current generator, or in the definition of the start time) throws an exception.

* Do not seen as an example coroutine regret, the most common use coroutine actually a generator.

1.4 An interesting library: pipe

I want this section of you a brief introduction pipe. Python is not a pipe built-in library, if you have installed easy_install, you can install it directly,
the reason to introduce this library, because it shows us a very innovative use of iterators and generators ways: flow. The pipe may be viewed as an iterative data stream, similar to Linux, the use of pipe '|' transmission data stream, and defines a set of "streaming" functions for receiving and processing the data stream, and the final output data stream again or the data stream is summed to give a result. Let's look at some examples.

The first, very simple to use add summation:

>>> from pipe import *
>>> range(5) | add
10

Number and the need to use courtship where, built-in functions similar to the role filter, display the required elements:

>>> range(5) | where(lambda x: x % 2 == 0) | add
6

Remember our definition of the Fibonacci sequence generation is it? Obtaining the number of columns and the need to use all the even take_while, the function of the same name itertools less than 10 000 have similar functions, elements taken until the condition is not satisfied:

>>> fib = fibonacci
>>> fib() | where(lambda x: x % 2 == 0)\
... | take_while(lambda x: x < 10000)\
... | add
3382

Elements required for a function application can use select, act like built-in functions map; need to get a list, you can use as_list:

>>> fib() | select(lambda x: x ** 2) | take_while(lambda x: x < 100) | as_list
[1, 1, 4, 9, 25, 64]

pipe also includes more stream processing function. You can even define their own stream processing function, only need to define a generator function and add decorators Pipe. Obtaining a definition of the following elements to index ineligible stream processing functions:

>>> @Pipe
... def take_while_idx(iterable, predicate):
... for idx, x in enumerate(iterable):
... if predicate(idx): yield x
... else: return
...

Using this stream handler acquired the first 10 digits of fib:

>>> fib() | take_while_idx(lambda x: x < 10) | as_list
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

More functions are not documented, you can view the source pipe, less than a total of 600 line in the file is a document which has 300 lines, the document contains a large number of examples.

pipe very simple to implement, use Pipe decorator, the common generator function (or returns an iterator function) agent on a common approach to achieve a __ror__ class instance can be, but this idea is really interesting .

Functional Programming Guide to the full text of all over here, I hope this series of articles to give you help. I hope people will see beyond some of the programmatic structure programming, and can skillfully use in the right place :)

I'll finish tomorrow impress a catalog for easy viewing, and list some reference articles. Unfortunately, these articles are almost always in English, please do your best to learn English, right - - #

Finally, we recommend a very wide python learning resource gathering, [click to enter] , here are my collection before learning experience, study notes, there is a chance of business experience, and calmed down to zero on the basis of information to project combat , we can at the bottom, leave a message, do not know to put forward, we will study together progress

Published 44 original articles · won praise 56 · views 60000 +

Guess you like

Origin blog.csdn.net/haoxun10/article/details/104908772