Python面试题之Python生成器
首先说明一下生成器也是迭代器,也有迭代器的那些优点。
那为什么要生成器呢?因为到目前为止都 不是你写的迭代器,都是别人定义好的。那如何自己去造一个迭代器呢?下面的内容就会给你答案。
想要自己造一个迭代器,我们可以根据迭代器的特征(只要一个对象有__iter__
和__next__
方法那它就是迭代器),自己定义一个类,然后定义一个__iter__()
和__next__()
, 然后这个类实例化的对象就是一个迭代器啦。
但是这样写太麻烦啦!何况我们现在还没有学到类的知识,怎么办?给你一个魔法棒,让你快速优雅高效地造一个迭代器。
yield关键字
第一种自造迭代器的方法就是使用yield
关键字。具体怎么实现呢?
非常简单,如下所示:
def g():
print("Hey~ 生成器")
yield 1
上面的写法非常类似于函数的定义,相当于把return换成了yield
(当然,并没有这么简单)。
此时,我执行g()
返回的就是一个生成器。就是这么简单。
ret = g()
print(ret) #输出<generator object g at 0x101fef6d0>
但是这里有个特别需要注意的地方,也是与函数最明显的区别:
我们执行g()
的时候,并没有打印"Hey~ 生成器"
,就像函数没执行一样。
这也是生成器一个非常重要的特点,那就是你执行g()
返回的是一个生成器,同时只有在迭代它(调用它的__next__()
)的时候它才开始执行内部代码,碰到yield
关键字就返回yield后面的值并停止。
print(ret.__next__()) $print(next(ret))
输出:
Hey~ 生成器
1
当然for循环它也是可以的:
for i in ret:
print(i)
输出:
Hey~ 生成器
1
yield
还可以多次执行,这与return也有区别。
def g2():
print("Hey~ 生成器1")
yield 1
print("Hey~ 生成器2")
yield 2 ret = g2()
此时,你执行下next(ret)
,会打印"嘿!生成器1"
,然后返回一个1
,再执行一次next(ret)
,会打印出"嘿!生成器2"
,然后返回一个2
。
print(next(ret))
# 输出
Hey~ 生成器1
1
print(next(ret))
# 输出
Hey~ 生成器2
2
yield与return的区别
在一个函数里return只能执行一次,return
之后函数就彻底结束了:
def test_return():
return 1
return 2 #永不执行
return 3 #永不执行
yield
之后可以保存函数的运行状态,下次继续执行:
def test_yield():
yield 1
yield 2 #下次next()后执行
yeild 3 #下次next()后执行
下面的例子中,使用return
时,只能返回0
。
def test_return2():
for i in range(10):
return i #只能返回0,函数就结束了
使用yield
能够依次返回0~9
。
def test_yield2():
for i in range(10):
yield i #每调用一次next()就会一次弹出0~9
yield的作用
yield
把函数变成了生成器(生成器就是迭代器)。- 为函数封装好了
__iter__
和__next__
方法,把函数的执行结果做成了迭代器。 - 遵循迭代器的取值方式 —
obj.__next__()
,触发的是函数的执行。函数暂停与继续执行的状态都是由yield
保存的。
倒计时的例子:
def countdown(n):
print("倒计时开始")
while n > 0:
yield n
n -= 1
print("发射")
分析下面语句的执行过程:
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
关键字时暂停:
g = countdown(5)
print(g.__next__())
输出:
倒计时开始
5
生成器也是不能后退:
g = countdown(5)
print(g.__next__())
print(g.__next__())
for i in g:
print(i)
输出:
倒计时开始
5
4
-- for --
3
2
1
发射
每调用一次countdown(5)
得到的都是不同的生成器
for i in countdown(5):
print(i) for i in copuntdown(5):
print(i)
输出:
5
5
下面的例子也是一样,每一次print中countdown(5)
都是一个全新的生成器,所以打印出来的值都是5
。
print(countdown(5).__next())
print(countdown(5).__next())
print(countdown(5).__next())
输出:
5
5
5
生成器表达式
我们之前学过列表推导式,是这样写的:
>>> [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
这样来得到一个元素数量较小的列表是非常方便的,但是如果要创建一个元素数量巨大的列表,就不那么友好了
>>> [i for i in range(10000000000)]
...
这个时候只要把[]
换成()
就把列表推导式 变成了生成器表达式,得到的就是一个生成器对象,就是这么神奇。
这就是第二种自造迭代器的方法。
>>> (i for i in range(10))
<generator object <genexpr> at 0x101fef6d0>
我们可以直接使用for循环遍历上面得到的生成器:
>>> for i in (i for i in range(10)):
... print(i)
...
0
1
2
3
4
5
6
7
8
9
这样我们就能自信的创建个10000000000元素的生成器,不担心内存会爆了。
>>> (i for i in range(10000000000))
<generator object <genexpr> at 0x101fef728>
最后的总结:
Python面试题之Python生成器的更多相关文章
- 千万不要错过这几道Python面试题,Python面试题No16
第1题: python下多线程的限制以及多进程中传递参数的方式? python多线程有个全局解释器锁(global interpreter lock),简称GIL,这个GIL并不是python的特性, ...
- Python面试题之Python面试题汇总
在这篇文章中: Python基础篇 1:为什么学习Python 2:通过什么途径学习Python 3:谈谈对Python和其他语言的区别 Python的优势: 4:简述解释型和编译型编程语言 5:Py ...
- python面试题之Python支持什么数据类型?
所属网站分类: 面试经典 > python 作者:外星人入侵 链接:http://www.pythonheidong.com/blog/article/67/ 来源:python黑洞网,专注py ...
- python面试题三:Python 网络编程与并发
1 简述 OSI 七层协议. OSI七层协议模型主要是: 应用层(Application):为用户的应用程序(例如电子邮件.文件传输和终端仿真)提供网络服务. 表示层(Presentation):使用 ...
- python面试题二:Python 基础题
1.位和字节的关系? Byte 字节 bit 位 1Byte = 8bit 2.b.B.KB.MB.GB 的关系? 1Byte = 8bit KB 1KB=1024B MB 1MB=1024KB GB ...
- Python面试题之python是一种什么语言及优缺点
1.说说python是一种什么语言? 参考答案:python是一门动态解释性的强类型定义语言 编译型vs解释型 编译型优点:编译器一般会有预编译的过程对代码进行优化.因为编译只做一次,运行时不需要编译 ...
- python面试题之Python是如何进行内存管理的
python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,即引用计数,当对象被创建时就创建了一个引用计数,当对象不再需要时,这个对象的引用计数为0时,它被垃圾回收. ...
- python面试题之python下多线程的限制
python多线程有个全局解释器锁(global interpreter lock). 这个锁的意思是任一时间只能有一个线程使用解释器,跟单cpu跑多个程序一个意思,大家都是轮着用的,这叫“并发”,不 ...
- Python面试题之Python中的类和实例
0x00 前言 类,在学习面向对象我们可以把类当成一种规范,这个思想就我个人的体会,感觉很重要,除了封装的功能外,类作为一种规范,我们自己可以定制的规范,从这个角度来看,在以后我们学习设计模式的时候, ...
随机推荐
- setTimeout 里 传递字符串代码报错
js高程 第三版 p203 重点:超时调用的代码都是在全局作用域中执行的,因此函数中this 的值在非严格模 式下指向window 对象,在严格模式下是undefined. 不过这里仅仅解释前半句: ...
- mongo 统计数据磁盘消耗
repl_test:PRIMARY> show dbsadmin 0.000GBdirect_vote_resource 16.474GBlocal 14.860GBpersonas 30.77 ...
- ES6中的let和const
let和const let 用来声明变量,但是所声明的变量只在let命令所在的代码块内有效 { let a=12 alert(a)//12 } alert(a)//报错 找不到 let不像var那样会 ...
- CSS语义化命名
CSS语义化命名 从上图我们可以大概看出这里有两种CSS的命名方式:1.结构化命名法:2.语义化命名法. 结构化命名法:根据页面中板块的位置而命名,如上图中的content-left,这时如果我们想把 ...
- 5.2 - ToDoList
一.ToDoList需求 参考链接http://www.todolist.cn/ 1.将用户输入添加至待办项 2.可以对todolist进行分类(待办项和已完成组),用户勾选既将待办项分入已完成组 3 ...
- 4.1 - FTP文件上传下载
题目:开发一个支持多用户同时在线的FTP程序要求:1.用户加密认证2.允许同时多用户登录3.每个用户有自己的家目录,且只能访问自己的家目录4.对用户进行磁盘配额,每个用户的可用空间不同5.允许用户在f ...
- 【opencv安裝】opencv2和opencv3共存——安装opencv2和opencv3到指定目录
安装 opencv2和opencv3共存会导致运行时问题,须分开 下载源码 cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/hom ...
- Linux的概念与体系(转)
学linux就用它了 http://www.cnblogs.com/vamei/archive/2012/10/10/2718229.html
- Loki之Funtion
阅读Loki中Funtion源码之后的个人理解,该库归纳起来可以说有三层(C++设计新思维列举到2个参数,此处列举到3个参数),要记住C++的模板其实就是C语言高级的宏定义,如果用户没有用到对应的模板 ...
- genymotion——在虚拟机中当中安装genymotion,启动已经新增好的设备时,提示:the virtual device got no ip address
1.启动已经新增好的设备时,提示:the virtual device got no ip address,于是在网上搜索该问题,便得到提示,先启动virtual box中的该模拟设备,于是便启动,出 ...