初学Python——列表生成式、生成器和迭代器
一、列表生成式
假如现在有这样一个需求:快速生成一个列表[1,2,3,4,5,6,7,8,9,10],该如何实现?
在不知道列表生成式的情况下,可能会这样写:
a=[1,2,3,4,5,6,7,8,9,10]
如果要每个值+1呢?可能会这样:
for index,i in enumerate(a):
a[index] +=1
print(a)
不够方便,这里讲一个快速生成列表的方法:列表生成式。意思就是立即生成列表。
生成一个1到10的列表:
a = [i+1 for i in range(10)]
print( a)
# output:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
生成一个2~20的偶数列表:
a=[ i*2 for i in rang(1,11)]
print(a)
# output:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
它相当于:
a=[]
for i in range(1,11): #列表生成式
a.append(i*2)
print(a)
生成的列表已经存在在内存中。
二、生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的列表,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
要创建一个生成器,只需要把列表生成式中的 [ ] 改成 ( ) 即可。
b=[i*2 for i in rang(10)] # 列表生成式
print(b) c=( i*2 for i in range(10) ) #生成器
print(c) # output:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
<generator object <genexpr> at 0x000001D0089B45C8>
输出c,得到的是数据类型说明和它的内存地址。
生成器只是名义上生成一个列表,但实际上却没有占用那么大内存,生成器只有调用的时候才会生成相应的数据。
如果要打印生成器的数据,则需要.__next__()方法
print(c.__next__()) # 输出第一个数
0
print(c.__next__()) # 输出第二个数
1
print(c.__next__()) # 输出第三个数
2
print(c.__next__()) # 输出第四个数
3
print(c.__next__()) # 输出第五个数
4
如果我只需要当中的最后一个数据呢?能不能直接输出?
抱歉,不能。而且,生成器的数据只能从前往后去访问,不能从后往前去访问,在内存中只保留一个值,也就是说,访问过的数据已经无法再次访问。
如果生成器有很多的数据,要全部输出,有没有简便的写法?
抱歉,没有,您只能一个一个地输出。
当然,像上面那样不断调用.__next__()还是太坑爹了,可以用for去迭代它(生成器也是可迭代对象):
g = (x * x for x in range(10))
for n in g:
print(n)
那,,我还要生成器有卵用??
还是有点卵用的,生成器一般依托于函数实现,比如,我先定义一个函数fib(),函数内定义了数列的推算规则
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done' # 注释:
a, b = b, a + b 相当于:
t = (b, a + b) # t是一个tuple
a = t[0]
b = t[1]
它不必写出显式变量 t
如果给fib()传参10,它将输出一连串的数字,可以组成一个数列:
1,1,2,3,5,8,13,21,34,55
此时的fib函数,已经非常接近生成器了,只需要一个yield即可,
def fib(max): #当函数中有yield出现时,不能将其简单视为函数,是一个生成器。
"生成器"
n,a,b=0,0,1
while n<max:
yield b #yield保存了函数当前的中断状态,返回当前b的值
a,b=b,a+b
n=n+1 #计数器
return "done"
此时,fib(10)是一个生成器,
f = fib(6)
print(f)
<generator object fib at 0x104feaaa0>
这里最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return
语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
来一个一个地输出它的值:
f=fib(10)
print(f)
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
因为只能一个一个地输出,且不能得知长度,所以总会有越界的时候,会报一个异常StopIteration,导致程序停止
所以需要捕获异常:
while 1:
try: #如果没有出现异常,执行下面语句
x=next(g)
print("g:",x)
except StopIteration as e: #如果出现异常StopIteration,把它赋给e,执行下面的语句
print(e.value)
break
前面讲到,生成器只能一个一个地取出数据,在fib函数执行过程中会中断,为什么要这样呢?有什么用吗?
它厉害在:可以在单线程的情况下实现并发效果,举个例子:
import time def custumer(name):
print("{0}准备来吃包子了".format(name))
while 1:
baozi = yield #每次运行到这一行时都会中断
print("包子{0}来了,被{1}吃掉了".format(baozi,name)) def producer(name):
c1=custumer("老大")
c2=custumer("老二")
c1.__next__()
c2.__next__() # next 只是在调用yield
print("{0}开始做包子啦!".format(name))
for i in range(1,15,2):
time.sleep(1)
print("做了两个包子")
c1.send(i) # send 调用yield的同时给它传值
c2.send(i+1) producer("alex")
如果在自己的解释器上执行,会发现一个程序有三个任务交错切换运行,看上去就像三个任务同时在进行。
三、迭代器
我们已经知道,可以直接作用于for
循环的数据类型有以下几种:
一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;
一类是generator
,包括生成器和带yield
的generator function。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
可以使用isinstance()
判断一个对象是否是Iterable
对象。
for循环本质上时不断调用next()函数实现的:
a=[1,2,3,4,5]
for x in a:
print(x)
#完全等价于
it=iter(a) # 将列表转化成迭代器对象
while 1:
try:
x=next(it) #获得下一个值
print(x)
except StopIteration:
break #遇到StopIteration异常就跳出循环
在文件操作时,
for line in f:
print(line)
每次输出其实都是调用next()函数,在Python3中已经看不出是一个迭代器了。
初学Python——列表生成式、生成器和迭代器的更多相关文章
- Python 列表生成式 生成器
[x for x in os.listdir("F:\XXX")] 生成器(x * x for x in range(10)) 如果列表元素按照某种算法推算出来,那我们就可以在循环 ...
- python协程函数应用 列表生成式 生成器表达式
协程函数应用 列表生成式 生成器表达式 一.知识点整理: 1.可迭代的:对象下有_iter_方法的都是可迭代的对象 迭代器:对象._iter_()得到的结果就是迭代器 迭代器的特性: 迭代器._n ...
- Python列表生成式(入门9)
转载请标明出处: http://www.cnblogs.com/why168888/p/6407984.html 本文出自:[Edwin博客园] Python列表生成式 1. 生成列表 L = [] ...
- Python 列表生成式 & 字典生成式
Python 列表生成式 & 字典生成式 通过生成式可以更加简洁地生成列表和字典 列表生成式 对比 直接生成数据后加入列表示例: user_list = list() for i in ran ...
- Python 列表生成式、生成器、迭代器
列表生成式 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式. 如果要生成[1x1, 2x2, 3x3, ..., 10x10]怎么 ...
- python 列表生成式,生成器&迭代器
列表生成式: 需求:要对列表 [0,1,2,3,4,5,6,7,8,9]的每个元素加1 用列表生成式一步搞定: li = [i+1 for i in range(10)] # 这种写法就叫列表生成式 ...
- Python之旅Day5 列表生成式 生成器 迭代器 装饰器
装饰器 器即函数,装饰即修饰,意指为其他函数添加新功能 装饰器定义:本质就是函数,功能是为其他函数添加新功能 装饰器涉及的知识点= 高阶函数+函数嵌套+闭包 在遵循下面两个原则的前提下为被装饰者新功能 ...
- python中的生成器,迭代器及列表生成式
列表生成器: 即List Comprehensions. 在python中,可通过内置的强大有简单的生成式来创建列表.例如创建一个1到10的列表list [1, 2, 3, 4, 5, 6, 7, ...
- Py修行路 python基础 (十二) 协程函数应用 列表生成式 生成器表达式
一.知识点整理: 1.可迭代的:对象下有_iter_方法的都是可迭代的对象 迭代器:对象._iter_()得到的结果就是迭代器 迭代器的特性: 迭代器._next_() 取下一个值 优点: 1.提供了 ...
随机推荐
- iphone手机投屏在哪里 手机无线投屏电脑
Iphone是我们经常使用的一款手机,有时候经常需要将一些文件图片信息等投屏到电脑,那么iphone手机投屏在哪里?可以无线投屏到电脑吗?其实很简单,下面就分享下苹果手机投屏的具体方法给大家,希望对大 ...
- Human Motion Analysis with Wearable Inertial Sensors——阅读1
Human Motion Analysis with Wearable Inertial Sensors——阅读 博主认为对于做室内定位和导航的人这是一篇很很棒的文章,不是他的技术很牛,而是这是一篇医 ...
- Android IPC机制(五)用Socket实现跨进程聊天程序
1.Socket简介 Socket也称作“套接字“,是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信.它分为流式套接字和数据包套接 ...
- 你不可不知的Java引用类型【总结篇】
四种引用类型总结 引用级别:强引用 > 软引用 > 弱引用 > 虚引用 理解 就如最开始说的,设置四种引用类型,是为了更好的控制对象的生命周期,让代码能够一定程度上干涉GC过程,所以 ...
- RESTful相关理解
just 一种接口规范,写法种类不止一种 类似python的那啥标准
- 洗礼灵魂,修炼python(87)-- 知识拾遗篇 —— 线程(1)
线程(上) 1.线程含义:一段指令集,也就是一个执行某个程序的代码.不管你执行的是什么,代码量少与多,都会重新翻译为一段指令集.可以理解为轻量级进程 比如,ipconfig,或者, python ...
- EOS智能合约开发(三):EOS创建和管理账号
没有看前面文章的小伙伴可以看一下 EOS智能合约开发(一):EOS环境搭建和启动节点 EOS智能合约开发(二):EOS创建和管理钱包 创建好钱包.密钥之后,接下来你就可以创建账号了,账号是什么?账号保 ...
- Objective-C简介
1.OC简介 全称:Objective-C,是扩充C的面向对象编程语言,主要用于iOS和Mac OS开发. C语言的基础上,增加了一层最小的面向对象语法 完全兼容C语言 可以在OC代码中混入C语言代码 ...
- win10系统中如何解决cmd中的路径和现在电脑的用户名不一致
假设原用户名老王,已删除,但是cmd后路径还是C:\Users\老王>,这与现在用户laowng不一致了需改为C:\Users\laowang>. .先新建一个管理员账户laowang,然 ...
- C语言 统计一篇英文短文中单词的个数
//凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ #include<stdio.h> #define N 1000 void main(){ ] ...