前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python面试题之Python生成器

Python面试题之Python生成器

作者头像
Jetpropelledsnake21
发布2019-02-15 16:10:35
1.1K0
发布2019-02-15 16:10:35
举报
文章被收录于专栏:JetpropelledSnake

首先说明一下生成器也是迭代器,也有迭代器的那些优点。

那为什么要生成器呢?因为到目前为止都 不是你写的迭代器,都是别人定义好的。那如何自己去造一个迭代器呢?下面的内容就会给你答案。

想要自己造一个迭代器,我们可以根据迭代器的特征(只要一个对象有__iter____next__方法那它就是迭代器),自己定义一个类,然后定义一个__iter__()__next__(), 然后这个类实例化的对象就是一个迭代器啦。

但是这样写太麻烦啦!何况我们现在还没有学到类的知识,怎么办?给你一个魔法棒,让你快速优雅高效地造一个迭代器。

yield关键字

第一种自造迭代器的方法就是使用yield关键字。具体怎么实现呢? 非常简单,如下所示:

代码语言:javascript
复制
def g():
    print("Hey~ 生成器")
    yield 1

 上面的写法非常类似于函数的定义,相当于把return换成了yield(当然,并没有这么简单)。

 此时,我执行g()返回的就是一个生成器。就是这么简单。

代码语言:javascript
复制
ret = g()
print(ret)  #输出<generator object g at 0x101fef6d0>

但是这里有个特别需要注意的地方,也是与函数最明显的区别:

我们执行g()的时候,并没有打印"Hey~ 生成器",就像函数没执行一样。 这也是生成器一个非常重要的特点,那就是你执行g()返回的是一个生成器,同时只有在迭代它(调用它的__next__())的时候它才开始执行内部代码,碰到yield关键字就返回yield后面的值并停止。

代码语言:javascript
复制
print(ret.__next__())  $print(next(ret))

输出:

代码语言:javascript
复制
Hey~ 生成器
1

当然for循环它也是可以的:

代码语言:javascript
复制
for i in ret:
    print(i)

 输出:

代码语言:javascript
复制
Hey~ 生成器
1

yield还可以多次执行,这与return也有区别。

代码语言:javascript
复制
def g2():
    print("Hey~ 生成器1")
    yield 1
    print("Hey~ 生成器2")
    yield 2

ret = g2()

此时,你执行下next(ret),会打印"嘿!生成器1",然后返回一个1,再执行一次next(ret),会打印出"嘿!生成器2",然后返回一个2

代码语言:javascript
复制
print(next(ret))
# 输出
Hey~ 生成器1
1
print(next(ret))
# 输出
Hey~ 生成器2
2

yield与return的区别

在一个函数里return只能执行一次,return之后函数就彻底结束了:

代码语言:javascript
复制
def test_return():
    return 1
    return 2    #永不执行
    return 3    #永不执行

yield之后可以保存函数的运行状态,下次继续执行:

代码语言:javascript
复制
def test_yield():
    yield 1
    yield 2    #下次next()后执行
    yeild 3    #下次next()后执行

下面的例子中,使用return时,只能返回0

代码语言:javascript
复制
def test_return2():
    for i in range(10):
        return i    #只能返回0,函数就结束了

使用yield能够依次返回0~9

代码语言:javascript
复制
def test_yield2():
    for i in range(10):
        yield i     #每调用一次next()就会一次弹出0~9

yield的作用

  1. yield把函数变成了生成器(生成器就是迭代器)。
  2. 为函数封装好了__iter____next__方法,把函数的执行结果做成了迭代器。
  3. 遵循迭代器的取值方式 — obj.__next__(),触发的是函数的执行。函数暂停与继续执行的状态都是由yield保存的。

倒计时的例子:

代码语言:javascript
复制
def countdown(n):
    print("倒计时开始")
    while n > 0:
        yield n
        n -= 1
    print("发射")

分析下面语句的执行过程:

代码语言:javascript
复制
g = countdown(5)
print(g.__next__())  # 打印"倒计时开始" 返回5 (此时n=5)
print(g.__next__())  # 返回4 (此时n=4)
print(g.__next__())  # 返回3 (此时n=3)
print(g.__next__())  # 返回2 (此时n=2)
print(g.__next__())  # 返回1 (此时n=1)
print(g.__next__())  # 打印"发射" 抛出StopIteration异常(此时n=0)

调用__next__()时函数执行内部代码,到yield关键字时暂停:

代码语言:javascript
复制
g = countdown(5)
print(g.__next__())

 输出:

代码语言:javascript
复制
倒计时开始
5

生成器也是不能后退:

代码语言:javascript
复制
g = countdown(5)
print(g.__next__())
print(g.__next__())
for i in g:
    print(i)

输出:

代码语言:javascript
复制
倒计时开始
5
4
-- for --
3
2
1
发射

每调用一次countdown(5)得到的都是不同的生成器

代码语言:javascript
复制
for i in countdown(5):
    print(i)
    
for i in copuntdown(5):
    print(i)

输出:

代码语言:javascript
复制
5
5

下面的例子也是一样,每一次print中countdown(5)都是一个全新的生成器,所以打印出来的值都是5

代码语言:javascript
复制
print(countdown(5).__next())
print(countdown(5).__next())
print(countdown(5).__next())

输出:

代码语言:javascript
复制
5
5
5

生成器表达式

我们之前学过列表推导式,是这样写的:

代码语言:javascript
复制
>>> [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

这样来得到一个元素数量较小的列表是非常方便的,但是如果要创建一个元素数量巨大的列表,就不那么友好了

代码语言:javascript
复制
>>> [i for i in range(10000000000)]
...

 这个时候只要把[]换成()就把列表推导式 变成了生成器表达式,得到的就是一个生成器对象,就是这么神奇。

这就是第二种自造迭代器的方法。

代码语言:javascript
复制
>>> (i for i in range(10))
<generator object <genexpr> at 0x101fef6d0>

 我们可以直接使用for循环遍历上面得到的生成器:

代码语言:javascript
复制
>>> for i in  (i for i in range(10)):
...     print(i)
... 
0
1
2
3
4
5
6
7
8
9

这样我们就能自信的创建个10000000000元素的生成器,不担心内存会爆了。

代码语言:javascript
复制
>>> (i for i in range(10000000000))
<generator object <genexpr> at 0x101fef728>

最后的总结:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-06-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • yield关键字
  • yield与return的区别
  • yield的作用
  • 生成器表达式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档