Home  >  Article  >  Backend Development  >  Detailed explanation of python generator in one article

Detailed explanation of python generator in one article

WBOY
WBOYforward
2022-06-09 16:02:172648browse

This article brings you relevant knowledge about python, which mainly introduces issues related to generators, including the concept of generators, the execution process of generators, yield and generation Container methods and other contents, let’s take a look at them together. I hope it will be helpful to everyone.

Detailed explanation of python generator in one article

Recommended learning: python video tutorial

This article brings you relevant knowledge about Python, including the main introduction We have discussed some issues related to generators, including the concept of generators, the execution process of generators, yield and generator methods, etc. Let’s take a look at them together. I hope it will be helpful to everyone.

1. Generator concept

Generator (English: generator) is a very fascinating thing and is often considered an advanced programming skill in Python. However, I am still

happy to discuss this topic with readers here - even though you may be a beginner - because I believe that the purpose of reading this tutorial is not to just limit yourself to Beginner level, you must have an unruly heart - to become a Python master. So, let’s start learning about generators.

Remember the "iterator" in the previous section? Generators and iterators have a certain origin relationship. The generator must be iterable. It is true that it is not just an iterator, but other than that, it does not have many other uses, so we can understand it as a very convenient automatic Define iterator.

2. Simple generator

>>> my_generator = (x*x for x in range(4))

Is this very similar to list analysis? Observe carefully, it is not a list. If you get it like this, it is a list:

>>> my_list = [x*x for x in range(4)]

The difference between the above two is [] or (). Although it is a small difference, the results are completely different.

>>> dir(my_generator)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__',
'__iter__',
'__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running',
'next',
'send', 'throw']

For easier observation, I have rearranged the above results. Have you discovered the necessary methods __inter__() and next() in iterators, which shows that it is an iterator. If it is an iterator, you can use a for loop to read out its values ​​in sequence

>>> for i in my_generator:
... print i
...
0
1
4
9
>>> for i in my_generator:
... print i
...

When it is looped for the first time, the values ​​​​in my_generator are read out and printed in sequence, but when it is read again , and found no results. This feature is also what iterators have.

For that list, it will be different:

>>> for i in my_list:
... print i
...
0
1
4
9
>>> for i in my_list:
... print i
...
0
1
4
9

Does the generator just replace [] in list parsing with ()? This is just a form of expression and usage of generators. Following the naming of

list analytic expressions, it can be called "generator analytic expression" (or: generator derivation, generator expression ).

Generator parsing expressions have many uses, and are a good choice to replace lists in many places. Especially when dealing with a large number of values, as mentioned in the previous section, lists occupy more memory. The advantage of iterators (generators are iterators) is that they occupy less memory, so there is no need to instantiate the generator (or iterator) Turn it into a list and operate it directly to show the advantages of iteration. For example:

>>> sum(i*i for i in range(10))
285

Pay attention to the sum() operation above. Don’t think that there is a parentheses missing, it’s just written like this. Isn't it charming? If the list, you have to

to have:

>>> sum([i*i for i in range(10)])
285

The generator obtained by the generator analysis is covered up some details of the generator, and the applicable areas are limited. Next, we will analyze the internals of the generator and gain a deeper understanding of this magical tool.

3. Definition and execution process

The word yield means "production, production" in Chinese. In Python, it is used as a keyword (you use it in variables, functions, classes

cannot be used in the name), which is the symbol of the generator.

>>> def g():
... yield 0
... yield 1
... yield 2
...
>>> g
<function g at 0xb71f3b8c>

Created a very simple function. The only difference from the functions we have seen before is the use of three yield statements. Then perform the following operations:

>>> ge = g()
>>> ge
<generator object g at 0xb7200edc>
>>> type(ge)
<type &#39;generator&#39;>

The return value of the function created above is an object of generator type.

>>> dir(ge)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']

I saw __iter__() and next() here, indicating that it is an iterator. In this case, of course it can:

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

As can be seen from this simple example, the return value of the function containing the yield keyword is an object of generator type, and this generator object is an iterator.

We call functions containing yield statements generators. A generator is an iterator defined with ordinary function syntax. As can be seen from the above example, during the definition process of this generator (also an iterator), __inter__() and next() are not written like the iterator in the previous section. Instead, as long as the yield statement is used, the ordinary function It magically becomes a generator and has the functional characteristics of an iterator.

The function of the yield statement is to return the corresponding value when called. Analyze the above running process in detail:

1. ge = g(): Except for returning the generator, there is no operation and no value is returned.

2. ge.next(): It is not until this time that the generator starts executing. When it encounters the first yield statement, it returns the value and pauses execution (some call it

为挂起)。

3. ge.next() :从上次暂停的位置开始,继续向下执行,遇到 yield 语句,将值返回,又暂停。

4. gen.next() :重复上面的操作。

5. gene.next() :从上面的挂起位置开始,但是后面没有可执行的了,于是 next() 发出异常。

从上面的执行过程中,发现 yield 除了作为生成器的标志之外,还有一个功能就是返回值。那么它跟 return 这个返回值有什么区别呢?

4. yield

为了弄清楚 yield 和 return 的区别,我写了两个函数来掩饰:

>>> def r_return(n):
... print "You taked me."
... while n > 0:
... print "before return"
... return n
... n -= 1
... print "after return"
...
>>> rr = r_return(3)
You taked me.
before return
>>> rr
3

从函数被调用的过程可以清晰看出, rr = r_return(3) ,函数体内的语句就开始执行了,遇到 return,将值返

回,然后就结束函数体内的执行。所以 return 后面的语句根本没有执行。这是 return 的特点

下面将 return 改为 yield:

>>> def y_yield(n):
... print "You taked me."
... while n > 0:
...     print "before yield"
...     yield n
...     n -= 1
...     print "after yield"
...
>>> yy = y_yield(3) #没有执行函数体内语句
>>> yy.next() #开始执行
You taked me.
before yield
3 #遇到 yield,返回值,并暂停
>>> yy.next() #从上次暂停位置开始继续执行
after yield
before yield
2 #又遇到 yield,返回值,并暂停
>>> yy.next() #重复上述过程
after yield
before yield
1
>>> yy.next()
after yield #没有满足条件的值,抛出异常
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
StopIteration

结合注释和前面对执行过程的分析,读者一定能理解 yield 的特点了,也深知与 return 的区别了。

一般的函数,都是止于 return。作为生成器的函数,由于有了 yield,则会遇到它挂起,如果还有 return,遇到它就直接抛出 SoptIteration 异常而中止迭代。

#!/usr/bin/env Python
# coding=utf-8

def fibs(max):
    """
    斐波那契数列的生成器
    """
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
if __name__ == "__main__":
    f = fibs(10)
    for i in f:
        print i ,

运行结果如下:

$ python 21501.py
1 1 2 3 5 8 13 21 34 55

用生成器方式实现的斐波那契数列是不是跟以前的有所不同了呢?大家可以将本教程中已经演示过的斐波那契数列实现方式做一下对比,体会各种方法的差异。

经过上面的各种例子,已经明确,一个函数中,只要包含了 yield 语句,它就是生成器,也是迭代器。这种方式显然比前面写迭代器的类要简便多了。但,并不意味着上节的就被抛弃。是生成器还是迭代器,都是根据具体的使用情景而定。

5. 生成器方法

在 python2.5 以后,生成器有了一个新特征,就是在开始运行后能够为生成器提供新的值。这就好似生成器

和“外界”之间进行数据交流。

>>> def repeater(n):
... while True:
...     n = (yield n)
...
>>> r = repeater(4)
>>> r.next()
4
>>> r.send("hello")
'hello

当执行到 r.next() 的时候,生成器开始执行,在内部遇到了 yield n 挂起。注意在生成器函数中, n = (yield

n) 中的 yield n 是一个表达式,并将结果赋值给 n,虽然不严格要求它必须用圆括号包裹,但是一般情况都这

么做,请大家也追随这个习惯。

当执行 r.send("hello") 的时候,原来已经被挂起的生成器(函数)又被唤醒,开始执行 n = (yield n) ,也就是

讲 send() 方法发送的值返回。这就是在运行后能够为生成器提供值的含义。

如果接下来再执行 r.next() 会怎样?

>>> r.next()

什么也没有,其实就是返回了 None。按照前面的叙述,读者可以看到,这次执行 r.next() ,由于没有传入任何值,yield 返回的就只能是 None.

还要注意,send() 方法必须在生成器运行后并挂起才能使用,也就是 yield 至少被执行一次。如果不是这样:

>>> s = repeater(5)
>>> s.send("how")
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator

就报错了。但是,可将参数设为 None:

>>> s.send(None)
5

这是返回的是调用函数的时传入的值。

此外,还有两个方法:close() 和 throw()

• throw(type, value=None, traceback=None):用于在生成器内部(生成器的当前挂起处,或未启动时在定

义处)抛出一个异常(在 yield 表达式中)。

• close():调用时不用参数,用于关闭生成器。

推荐学习:python视频教程

The above is the detailed content of Detailed explanation of python generator in one article. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:csdn.net. If there is any infringement, please contact admin@php.cn delete