pythonfor遍历列表运算_Python之路(第十篇)迭代器协议、for循环机制、三元运算、列表解析式、生成器...

一、迭代器协议

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)

输出结果

分析:这里可以看到,对字典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]

at 0x02221600>

分析:这里的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]

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))

输出结果

函数内的第一处 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

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

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

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

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

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异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值