python生成器

1.迭代

在理解生成器之前,先理解迭代。

1.1 迭代

如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)

alist = [1, 2, 3, 4, 5]
for i in alist:
    print(i)
1
2
3
4
5

正如将列表中的元素通过for循环,遍历了整个alist列表,这种不重复地便利其内部的每一个子项的行为就是迭代。

1.2 可迭代对象

可以直接作用于for循环的对象统称为可迭代对象:Iterable,可迭代对象一般都实现了__iter()__方法,可迭代对象通过其内建的方__iter()__返回一个迭代器对象。

a_iterable = [1, 2, 3]
a_iterator = iter(a_iterable)  # 将可迭代对象转化为迭代器
next(a_iterator)
1
next(a_iterator)
2
next(a_iterator)
3

1.3 迭代器

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator,迭代器其内实现了__iter__方法和__next__方法,for循环本质是通过调用可迭代对象的__iter__方法,该方法返回一个迭代器对象,再用__next__方法遍历元素

定义一个迭代器:

class MyRange:
    def __init__(self, end):
        self.index = 0
        self.end = end
        
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index < self.end:
            val = self.index
            self.index += 1
            return val
        else:
            raise StopIteration()
my_range = MyRange(3)
print([i for i in my_range])
[0, 1, 2]
print([i for i in my_range])
[]

迭代器只能迭代一次,每次调用调用 next() 方法就会向前一步,不能后退,所以当迭代器迭代到最后时,就不可以重复利用,所有需要将迭代器和可迭代对象分开定义

修改上面的可迭代对象:

class MyRange:
    def __init__(self, end):
        self.end = end
        
    def __iter__(self):
        return MyIterator(self.end)
    
class MyIterator:
    def __init__(self, end):
        self.index = 0
        self.end = end
        
    def __iter__(self):
        return self    
    
    def __next__(self):
        if self.index < self.end:
            val = self.index
            self.index += 1
            return val
        else:
            raise StopIteration()
my_range = MyRange(3)
print([i for i in my_range])
[0, 1, 2]
print([i for i in my_range])
[0, 1, 2]

2. 生成器

生成器与可迭代对象、迭代器的关系

python生成器_第1张图片

图片来自 Iterables vs. Iterators vs. Generators

生成器对象,在每次调用它的next()方法时返回一个值,直到它抛出StopInteration。

生成器是可以迭代的,但是你 只可以读取它一次 ,因为它并不把所有的值放在内存中,它是实时地生成数据, 可以用生成器表达式创建:

my_generator = (x ** 2 for x in range(3))
my_generator
 at 0x7f975b7a4af0>
for i in my_generator:
    print(i)
0
1
4

yield

可以写一个普通的包含yield语句的Python函数,Python会检测对yield的使用并将函数标记为一个生成器,当函数执行到yield语句时,像return语句那样返回一个值,但是解释器会保存对栈的引用,它会被用来在下一次调用next时恢复函数。

def my_generator():
    yield 1
    yield 2
    yield 'a'
    yield 'generator'
g = my_generator()
g

next(g)
1
next(g)
2
next(g)
'a'
next(g)
'generator'
next(g)
---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

 in ()
----> 1 next(g)


StopIteration: 

上面的例子中,每次调用next()开始实时地生成数据,并返回,因此生成器只可读取一次,上次执行读取的值在下次执行中就无法读取。当整个生成器的值都被读取后,在调用机会出现StopIteration的错误。

def my_gen():
    for i in range(5):
        yield i ** 3
my_gen()

mygen = my_gen()
for i in mygen:
    print(i)
0
1
8
27
64

每次执行到yield语句,则返回一个值,再执行的时候从上次停下来的地方开始执行。yield语句保存了上次执行后的状态,下次执行不是从头开始,而是从上次的状态开始。

当调用my_gen()这个函数的时候,函数内部的代码不会立即执行,而是返回一个生成器对象,当利用for循环进行遍历的时候,函数内部的代码开始执行,执行到yield表达式返回一个值,记录当前状态并停下,下一次的访问时再从这个状态开始执行。

举一个不太恰当的例子,普通的函数就是没有存档的游戏,只要游戏开始,就玩到结尾,下一次再玩还是从头开始,而生成器就是加了存档,下次玩从上次存档的地方开始

关于生成器的思考

(瞎掰的。。。。)生成器到底起到什么吗作用呢,就算生成一个生成器对象,而生成器对象一定是一个迭代器,所以可以这么说,生成器返回了一个可以用for循环遍历所以子项,可以用next()方法访问下一个子项,可以在访问时动态的生成数据而节省内存的对象。

阅读

完全理解 Python 迭代对象、迭代器、生成器
对 Python 迭代的深入研究
Python迭代器和生成器
3. (译)Python关键字yield的解释(stackoverflow)
Python之列表生成式、生成器、可迭代对象与迭代器


你可能感兴趣的:(python生成器)