打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
【Python之路】特别篇
生成器(constructor)

  生成器函数在Python中与迭代器协议的概念联系在一起。包含yield语句的函数会被特地编译成生成器 !!!

  当函数被调用时,他们返回一个生成器对象,这个对象支持迭代器接口。

  不像一般的函数会生成值后退出,生成器函数在生成值后会自动挂起并暂停他们的执行和状态,他的本地变量将保存状态信息,这些信息在函数恢复时将再度有效

创建生成器方式有两种:

方法一:

1
s = ( x for x in range(5) )

方法二:

1
2
3
def foo():
    print('OK')
    yield 1

例子:

1
2
3
4
5
6
def g(n):
    for i in range(n):
        yield i **2
for i in g(5):
    print(i)

要了解他的运行原理,我们来用next方法看看:

1
2
3
4
5
6
7
8
9
10
11
t = g(5)
print(t.__next__())      # 0
print(t.__next__())      # 1
print(t.__next__())      # 4
print(t.__next__())      # 9
print(t.__next__())      # 16
print(t.__next__())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

在运行完5次next之后,生成器抛出了一个StopIteration异常,迭代终止。

send(msg) 与 next()

了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。

其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做

c.next() 和 c.send(None) 作用是一样的。

1
2
3
4
5
6
7
8
9
10
11
def g(n):
    for i in range(n):
        ret = yield i **2
        print(ret)
t = g(5)
print(t.__next__())
print(t.send('Hello'))
# 0 Hello 1

需要注意的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有yield语句来接收这个值。

send(msg) 和 next()是有返回值的,它们的返回值很特殊,返回的是yield表达式的参数 !

执行顺序是:遇到yield 先返回值,等下次再进入时再用 msg 进行赋值!!!

再来看一个yield的例子,用生成器生成一个Fibonacci数列:

1
2
3
4
5
6
7
8
9
10
def fab(max):
    a, b = 0, 1
    while a < max:
        yield a
        a, b = b, a + b
for i in fab(20):
    print(i)
# 0 1 1 2 3 5 8 13

另一个 yield 的例子来源于文件读取。

1
2
3
4
5
6
7
8
9
def read_file(fpath):
   BLOCK_SIZE = 1024
   with open(fpath, 'rb') as f:
       while True:
           block = f.read(BLOCK_SIZE)
           if block:
               yield block
           else:
               return

  

迭代器(iterator)

  for循环可以用于Python中的任何类型,包括列表、元祖等等,实际上,for循环可用于任何“可迭代对象”!

  迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration。

  任何这类的对象在Python中都可以用for循环或其他遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,并且捕捉StopIteration异常来确定何时离开。

  使用迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会造成内存的过大开销。

注意

  判断迭代器的条件是:

  1. 有__iter__ 方法

  2. 有__next__ 方法

  所有的生成器都是迭代器!

迭代器例子:

比如要逐行读取一个文件的内容,利用readlines()方法,我们可以这么写:

1
2
for line in open("test.txt").readlines():
    print (line)

这样虽然可以工作,但不是最好的方法。因为他实际上是把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了。

利用file的迭代器,我们可以这样写:

1
2
for line in open("test.txt"):
    print (line)

这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。

for 循环实质

  1. 调用 __iter__ 方法将可迭代对象转换成迭代器

  2. 对迭代器对象不断调用 __next__ 方法

  3. 处理StopIteration 异常

View Code

 

可迭代对象(iterable)

  判断条件:内部具有 __iter__ 方法

1
2
3
4
5
6
from collections import Iterable,Iterator
print(isinstance(range(1),Iterable))
print(isinstance(range(1),Iterator))
print(isinstance(list(),Iterable))
print(isinstance(list(),Iterator))

  

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Python生成器和迭代器有什么用?
Python迭代和生成操作小结
Python第六章-函数05-迭代器&生成器
Iterator迭代器和生成器
Python迭代器定义及用法详解
python 生成器 & 迭代器
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服