一、迭代器协议

a迭代的含义

  迭代器即迭代的工具,那什么是迭代呢?
#迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值

b为何要有迭代器?

对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器

c可迭代对象

可迭代对象指的是内置有iter方法的对象,即字符串、元组、列表、集合、字典、文件,

  'hello'.__iter__
(1,2,3).__iter__
[1,2,3].__iter__
{'a':1}.__iter__
{'a','b'}.__iter__
open('a.txt').__iter__

d迭代器对象

  可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象

可迭代对象(字符串、元组、列表、集合、字典、文件)通过调用

  __iter__()

方法,这里是遵循迭代器协议,将可迭代对象转为一个迭代器,这时既可以调用

  __iter__()方法又内置有__next__()方法

即为迭代器对象。迭代器对象是一个内存地址。

迭代器对象本身也可以使用__iter__()方法

迭代器对象再次使用__iter__()方法生成的还是迭代器对象。

例子

  
  dic = {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"}
iter_dic = dic.__iter__()
print(iter_dic)
v =iter_dic.__iter__()
print(v)

  

输出结果

  
  <dict_keyiterator object at 0x02191600>
<dict_keyiterator object at 0x02191600>

  


分析:这里可以看到,对字典dic调用了__iter__()方法,使其变成迭代器对象,再次对这个迭代器对象使用__iter__()方法还是其本身。

e迭代器协议

1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退)

2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个iter()方法)

3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。

f注意:

迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象。

例子

  s  = "hello"
iter_s = s.__iter__() #将字符串用__iter__()方法转换为迭代器对象
print(iter_s.__next__()) #调用__next__()方法依次按照顺序打印每个字符
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__()) #抛出异常StopIteration,或者说结束标志

  

输出结果

  
  h
e
l
l
o
#抛出异常StopIteration,或者说结束标志,StopIteration

  

这里等同于用for循环打印

  s  = "hello"
for i in s: #for i in s.__iter__()
print(i) #print(iter_s.__next__())直到出现StopIteration,然后结束循环

  

分析:这里的for 循环里的for i in s,s调用了__iter__()方法,将s变为一个迭代器对象,同时对这个迭代器对象使用

__next__()方法打印出来,循环访问,并处理了最后的StopIteration,结束了循环。

小知识

next()方法是调用python解释器的,等同于某个可迭代对象下的__next__()方法

  
  #print(next(iter_s))等同于print(iter_s.__next__())

  

例子2

  
  dic = {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"}
iter_dic = dic.__iter__()
print(iter_dic.__next__())
print(iter_dic.__next__())
print(iter_dic.__next__())
print(iter_dic.__next__())
# print(iter_dic.__next__()) 产生StopIteration停止标志

  

输出结果

  
  k1
k2
k3
k4

  

改成for循环

  
  dic = {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"}
for i in dic: #dic调用了__iter__方法,将其改成迭代器对象
print(i) #使用__next__()方法挨个去打印,直到出现StopIteration结束

  

用while循环实现

  
  dic = {"k1":"v1","k2":"v2","k3":"v3","k4":"v4"}
iter_dic = dic.__iter__()
while True:
try:
print(iter_dic.__next__())
except StopIteration:
print("迭代结束了,循环终止")
break

  

输出结果

  
  k1
k2
k3
k4
#迭代结束了,循环终止

  

g迭代器的优缺点

优点:

  • 提供一种统一的、不依赖于索引的迭代方式

  • 惰性计算,节省内存

缺点:

  • 无法获取长度(只有在next完毕才知道到底有几个值)

  • 一次性的,只能往后走,不能往前退

二、三元运算

三元表达式的格式

  为真时的结果 if 判定条件 else 为假时的结果

如果条件成立,返回if前面的结果,否则else 返回else后的结果

例子

  
  a = 2
b = 3
s = a if a < b else b #这里的if语句后不加冒号
print(s)

  

输出结果

  
  2

  

例子2

  
  name = input('姓名>>: ')
res = 'SB' if name == 'ken' else 'NB'
print(name,res)

  

三、列表解析式

列表解析是Python迭代机制的一种应用,它常用于实现创建新的列表,返回的是一个列表,因此用在[]中。

例子

生成1-100以内的偶数

普通使用for循环的方式

  
  li  = []
for i in range(1,101):
if i % 2 == 0:
li.append(i)
else:
pass
print(li)

  

使用列表解析式

  
  li  = []
res = [i for i in range(1,101)if i % 2 == 0 ]
print(res)

  

例子2

将字符串变大写组成列表

普通方式

  
  s  = "nicholas"
li = []
for i in s :
res = i.upper()
li.append(res)
print(li)

  

列表解析式

  
  s  = "nicholas"
li = [i.upper() for i in s ]
print(li)

  

四、生成器

如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。

第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator,即生成器表达式:

生成器表达式能做的事情列表解析基本都能处理,只不过在需要处理的序列比较大时,列表解析比较费内存。

例子

  
  li = [i*i for i in range(10) ]
print(li)

  

输出结果

  
  [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

  

分析:这里是生成了一个0到9的平方的列表。

这里要改成生成器只要把列表生成式的[]改成()

  
  li = [i*i for i in range(10) ]
g = (i*i for i in range(10) )
print(li)
print(g)

  

输出结果

  
  [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x02221600>

  

分析:这里的<generator object <genexpr> at 0x02221600>就是一个生成器。这是生成器的第一种形式。

生成器是包含有__iter__()__next__()方法的,所以可以直接使用for来迭代

在这里调用__next__()方法或者用next()直接打印


  li = [i*i for i in range(5) ]
g = (i*i for i in range(5) )
print(li)
print(g)
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(next(g))
print(next(g))
#print(next(g)) #执行到此处就会产生一个StopIteration错误,类似可迭代对象调用__next__()一样

  

输出结果

  
  [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x02211600>
0
1
4
9
16

  

这里的g生成器就是一个迭代器。

这里也可以用for循环直接打印

li = [i*i for i in range(5) ]
g = (i*i for i in range(5) )
print(li)
for i in g:
print(i)

  

输出结果

[0, 1, 4, 9, 16]
0
1
4
9
16

 

分析:同样的这里的对生成器g的for循环自动处理了StopIteration错误,结束了for循环。与处理可迭代对象的方式类似。

第二种是生成器函数:

在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数。生成器函数可以生产一个无线的序列,这样列表根本没有办法进行处理。

yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator。函数名+括号就变成了生成器。

例子

下面为一个可以生产奇数的生成器函数。

def num():
n=1
while True:
print("函数内的第一处",n)
yield n
print("函数内奇数", n)
n+=2
print("函数内的第二处", n)
new_num = num()
print(new_num)
next(new_num)
print("函数外面的",next(new_num))

  

输出结果

<generator object num at 0x02421660>
函数内的第一处 1
函数内奇数 1
函数内的第二处 3
函数内的第一处 3
函数外面的 3

  

分析:执行print(new_num)语句可以看到,这里的自定义函数是一个生成器,通过next()方法调用执行函数内部语句,这里的yield相当于return的功能,每次next()返回一个迭代值,下次next()继续执行循环,于是函数继续执行,直到再次遇到 yield,再次返回。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。而不是在while True语句下一次性执行完语句。

yield 与 return

return 是返回并中止函数

yield 是返回数据,并冻结当前的执行过程

next唤醒冻结的函数执行过程,继续执行,直到遇到下一个yield

例子

def g():
yield 1
yield 2
yield 3
new_g = g()
print(next(new_g))
#第一次调用next(new_g)时,会在执行完yield语句后挂起,所以此时程序并没有执行结束,函数返回数据1,并冻结当前执行过程,等待下一个next()唤醒执行过程
print(next(new_g))
#通过next()唤醒执行过程,yield返回数据2,冻结执行过程,等待下一个next()
print(next(new_g))
# print(next(new_g))
#这里如果运行上面这条语句,程序试图从yield语句的下一条语句开始执行,发现已经到了结尾,所以抛出StopIteration异常。

  

输出结果

1
2
3

  

分析:在一个生成器中,如果没有return,则默认执行到函数完毕时返回StopIteration;
如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。

例子

def g():
yield 1
yield 2
return "a"
yield 3
new_g = g()
print(next(new_g))#通过next()执行函数,得到返回数据1,冻结当前过程,等待下一个next()唤醒
print(next(new_g))#通过next()执行函数,得到返回数据1,冻结当前过程,等待下一个next()唤醒
print(next(new_g))#通过next()执行函数,遇到return语句,直接抛出StopIteration 终止迭代,这样yield '3'语句永远也不会执行。
print(next(new_g))

  

输出结果

1
Traceback (most recent call last):
2
File "D:/exercise/test1.py", line 11, in <module>
print(next(new_g))
StopIteration: a #如果在return后返回一个值,那么这个值为StopIteration异常的说明,不是程序的返回值。

  

如果在return后返回一个值,那么这个值为StopIteration异常的说明,不是程序的返回值。

生成器没有办法使用return来返回值。

例子

与上面的例子基本相同,只是修改了return的返回值

def g():
yield 1
yield 2
return "some"
yield 3
new_g = g()
print(next(new_g))
print(next(new_g))
print(next(new_g))
print(next(new_g))

输出结果

1
Traceback (most recent call last):
2
File "D:/exercise/test1.py", line 11, in <module>
print(next(new_g))
StopIteration: some #生成器return返回的值是为StopIteration异常的说明,不是程序的返回值。

  

生成器支持的方法
close()

手动关闭生成器函数,后面的调用会直接返回StopIteration异常。

例子

def g():
yield 1
yield 2
yield 3 new_g = g()
print(next(new_g))
print(next(new_g))
new_g.close() #这里通过cloes()方法直接关闭了生成器,后续通过next()也无法唤醒生成器继续执行返回数据,也就是说无法返回数据3,在这里直接抛出StopIteration异常
print(next(new_g))
print(next(new_g))

 

输出结果

1
Traceback (most recent call last):
2
File "D:/exercise/test1.py", line 11, in <module>
print(next(new_g))
StopIteration

  

send()方法

生成器函数最大的特点是可以接受外部传入的一个变量,并根据变量内容计算结果后返回。

例子

def gen():
value = 0
while True:
receive = yield value
if receive == "stop":
break
value = 'got: %s' % receive

g=gen()
print(g.send(None))
print(g.send('aaa'))
print(g.send(3))
print(g.send('stop'))

  

输出结果

  
  Traceback (most recent call last):
File "D:/exercise/test6.py", line 18, in <module>
print(g.send('stop'))
StopIteration
0
got: aaa
got: 3

  

分析:

执行过程:

1、首先g.send(None)或者g.next()唤醒生成器,并执行到receive = yield value语句,冻结执行过程,等下一个next()、g.send()、g.close(),此时,执行完了yield语句,但是没有给receive赋值。

注意:在启动生成器函数时只能send(None)或者next(g),如果试图输入其它的值都会得到错误提示信息。

2、通过g.send('aaa'),会传入aaa,并赋值给receive,然后计算出value的值,并回到while头部,执行yield value语句有停止。

此时yield value会输出"got: aaa",然后冻结执行状态。

3、通过g.send(3),会重复第2步,最后输出结果为"got: 3"

4、通过g.send('stop'),传入生成器函数,赋值给receive,执行if receive == "stop":break 退出循环,整个函数执行完毕,最后出现StopIteration异常

throw()

throw()用来向生成器函数送入一个异常,可以结束系统定义的异常,或者自定义的异常。throw()后直接抛出异常并结束程序,或者消耗掉一个yield,或者在没有下一个yield的时候直接进行到程序的结尾。

例子

def gen():
while True:
try:
yield 'normal value'
yield 'normal value 2'
print('here')
except ValueError:
print('we got ValueError here')
except TypeError:
break
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(next(g))
print(next(g))
print(g.throw(TypeError))

  

输出结果

  normal value
we got ValueError here
normal value
normal value 2
here
normal value
normal value 2
Traceback (most recent call last):
File "D:/exercise/test6.py", line 22, in <module>
print(g.throw(TypeError))
StopIteration

  

分析:

执行过程

1、通过print(next(g))唤醒生成器,执行到yield 'normal value',返回"normal value"被输出,冻结生成器函数执行过程

2、执行print(g.throw(ValueError))语句,出现了ValueError,这里直接执行了except ValueError下的内容,循环继续,返回try语句,执行了yield 'normal value',这里又输出了一个“normal value”,之后冻结执行过程

3、执行了print(next(g)),函数里执行yield 'normal value 2'语句,输出“normal value 2”,冻结执行过程,

4、执行了print(next(g)),函数里开始继续执行,输出“here”,之后调到开始执行yield 'normal value',输出“normal value”,冻结执行过程

5、执行了print(next(g)),函数里执行yield 'normal value 2',输出“normal value 2”,冻结执行过程

6、通过print(g.throw(TypeError)),跳出try语句,出现TypeError,直接执行except TypeError:

跳出while循环,到达生成器函数结尾,所以抛出StopIteration异常。

最新文章

  1. 如何正确做 Web端压力测试?
  2. mybatis中#{}和${}的区别
  3. 452. Minimum Number of Arrows to Burst Balloons——排序+贪心算法
  4. xp默认安装路径及本地用户配置文件存放路径修改
  5. Scut 进阶:EntityChangeEvent
  6. HttpCookie
  7. UVa 821 Page Hopping
  8. windows(win10)下的mysql解压版安装
  9. 关于MAC设置免费的动态壁纸
  10. BeanFactory VS FactoryBean
  11. 表达式求值(栈方法/C++语言描述)(二)
  12. WPF布局控件与子控件的HorizontalAlignment/VerticalAlignment属性之间的关系
  13. 在AspNetCore中使用极验做行为认证
  14. linux服务器上配置多个svn仓库
  15. MySQL慢查询语句的定位
  16. debian的python蓝牙库
  17. go module 命令
  18. win7(x64)安装scrapy框架
  19. 【内核】探究linux内核,超详细解析子系统
  20. 国内Ubuntu镜像源

热门文章

  1. 生产环境nginx上传文件报错413 Request Entity Too Large
  2. JMeter (二十)参数化、检查点、集合点(转载)
  3. V4 V7 V13支持包的区别(转)
  4. python之列表、元组、字典学习
  5. pta_l1-6(连续因子)
  6. Ubuntu下ssh的安装
  7. NumPy 排序、条件刷选函数
  8. fullCalendar插件基本使用
  9. ADB 运行原理
  10. AltiumDesigner 网络标号放置技巧