5分钟掌握Python—yield关键字

321 阅读4分钟

yield类似于一种暂停并将值返回,直到下一次调用时都不会执行之后的代码

内置函数next()可以让yield执行到下一次

迭代器可以使用next()方法来使他依次输出,迭代器中的值

请看下面的一个简单的示例:

def test():
    # 这里创建了一个序列,并且有三个元素,序列可以作为迭代器被循环
    basket = ['name', 'quest', 'favorite color']

    for item in basket:
        yield item 
    print('_______轮到我了________')

# 由于test函数中有yield关键字,所以这里会返回一个迭代器
a = test()
print(a)

print(next(a))
print(next(a))
print(next(a))
print(next(a))
# 控制台输出
<iterator object at 0x00A1DB50> # 这个字符表示a时一个迭代器

name # 第一次调用next
quest # 第二次调用next
favorite color # 第三次调用next

# 第四次调用next,迭代器中只有三个元素,所以第四次调用的时候会执行之后的代码,
# 所以我们看到了`_______轮到我了________`的输出,同时迭代器结束了,所以会输出`StopIteration`
_______轮到我了________
Traceback (most recent call last):
  File "/home/tourpy/tourpy/spiders/__init__.py", line 18, in <module>
    print(next(a))
StopIteration

再上述代码中我们可以使用for in循环来代替多次调用next的写法,例如

# 内部会调用迭代器相关的方法
for c in a:
    print(c)

看完上面的解释之后我们再来理解官方的释义会更加方便

迭代器

到大多数容器对象都可以使用 for 语句:

for element in [1, 2, 3]:
    print(element)
for element in (1, 2, 3):
    print(element)
for key in {'one':1, 'two':2}:
    print(key)
for char in "123":
    print(char)
for line in open("myfile.txt"):
    print(line, end='')

这种访问风格清晰、简洁又方便。 迭代器的使用非常普遍并使得 Python 成为一个统一的整体。 在幕后,for 语句会在容器对象上调用 iter()。 该函数返回一个定义了 next() 方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,next() 将引发 StopIteration 异常来通知终止 for 循环。 你可以使用 next() 内置函数来调用 next() 方法;这个例子显示了它的运作方式:

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    next(it)
StopIteration

看过迭代器协议的幕后机制,给你的类添加迭代器行为就很容易了。 定义一个 iter() 方法来返回一个带有 next() 方法的对象。 如果类已定义了 next(),则 iter() 可以简单地返回 self:

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)

    def __iter__(self):
        return self

    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]
>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...     print(char)
...
m
a
p
s

生成器

Generator 是一个用于创建迭代器的简单而强大的工具。 它们的写法类似标准的函数,但当它们要返回数据时会使用 yield 语句。 每次对生成器调用 next() 时,它会从上次离开位置恢复执行(它会记住上次执行语句时的所有数据值)。 显示如何非常容易地创建生成器的示例如下:

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]
# for in 循环迭代器的时候内部会自动调用next()
>>> for char in reverse('golf'):
...     print(char)
...
f
l
o
g

可以用生成器来完成的操作同样可以用前一节所描述的基于类的迭代器来完成。 但生成器的写法更为紧凑,因为它会自动创建 iter() 和 next() 方法。

另一个关键特性在于局部变量和执行状态会在每次调用之间自动保存。 这使得该函数相比使用 self.index 和 self.data 这种实例变量的方式更易编写且更为清晰。

除了会自动创建方法和保存程序状态,当生成器终结时,它们还会自动引发 StopIteration。 这些特性结合在一起,使得创建迭代器能与编写常规函数一样容易。