可迭代对象、迭代器对象、生成器对象
可迭代对象、迭代器对象、生成器对象
可迭代对象
可迭代对象都有一个特点,对象内置__iter__
方法,可以通过dir(对象)
查看,也可以通过pycharm,在变量名后点一下.
查看有没有这个内置方法。
可迭代对象的分类
-
不可迭代的对象
int float bool 函数对象
-
可迭代对象
str list dict tuple set 文件对象
可迭代的含义
迭代,依赖上一次的结果,来更新下一次。
如以下一段代码:
count = 1
total = 0
while count < 10:
total += 1 # debug这段代码,每次循环中total都根据上一次更新
count +=1
print(total) # 45
迭代器对象
可迭代对象通过__iter__
方法产生的对象叫做的迭代器对象。
迭代器对象拥有__iter__
方法和__next__
方法。
迭代器对象提供了一种不依赖索引取值的方式,就是依次的取出迭代值。
迭代器对象操作
s1 = 'hello' # 可迭代对象
res = s1.__iter__() # 通过iter产生迭代器对象
print(res.__next__()) # h # next指迭代向后一个个取值
print(res.__next__()) # l # 每执行一次next就向后迭代一次。
# 上述代码中,是可迭代对象的iter方法
# 而迭代器的iter方法是用来干什么的呢。
s1 = 'hello' # 可迭代对象
res = s1.__iter__().__iter__() # 通过iter产生迭代器对象,再次执行iter
print(res.__next__()) # h # 依然可以通过next取值
# 迭代器通过__iter__还是迭代器
注意:一旦__next__取不到值了,就会报错
for 循环的本质
for 变量名 in 可迭代对象\迭代器对象:
循环体代码
- 对in后面的对象执行
__iter__
方法得到迭代器对象 - while循环调用
__next__
取值,赋值给变量名 - 取到最后,next取不出来的报错会被捕获,这个要涉及到异常捕获
生成器对象
生成器对象是迭代器对象的一种,它也具有__iter__
方法和__next__
方法。
一般的迭代器对象是解释器提供的,它们可能通过可迭代对象得到。
生成器对象可由程序员自己编写,通过函数定义和关键字yield来构造。
创建生成器的语法
当在函数体中塞入yield,函数调用就变成了得到生成器
def my_iter(): # 定义函数,先不执行
print('第一步')
yield # 分割了每一步
print('第二步')
yield
print('第三步')
yield
print('第四步')
yield
res = my_iter() # 拿到生成器
res.__next__() # 执行到第一个yield停
res.__next__() # 执行到第二个yield停
res.__next__() # 执行到第三个yield停
res.__next__() # 执行到第四个yield停
yield关键字还可以像return一样返回值
def my_iter():
yield 111
yield 222
yield 333
yield 444
res = my_iter()
print(res.__next__()) # 111
print(res.__next__()) # 222
print(res.__next__()) # 333
print(res.__next__()) # 444
进阶训练:用生成器实现range的功能
# 一个参数
def my_range1(end):
num = 0
while num < end:
yield num
num += 1
for i in my_range2(10):
print(i)
# 两个参数
def my_range2(start, end=None):
if not end: # 如果没有第二个参数,则第一个参数是end
end = start
start = 0
num = start
while num < end:
yield num
num += 1
for i in my_range2(1, 10): # 一个参数或者两个参数都可以
print(i)
# 完整功能
def my_range(start, end=None, internal=1): # 步长默认为1
num = start
if not end:
end = start
num = 0
if internal >= 0:
while num < end:
yield num
num += internal
else: # 步长为负时的情况
while num > end:
yield num
num += internal
for i in my_range(44, 66, 3):
print(i)
-
yield得到参数的方法(了解)
def depart(): print('接下来选手们将依次出发') while True: troop = yield # 外部执行send时,从这里赋值给troop print(f'{troop}出发啦!') gogogo = depart() gogogo.__next__() gogogo.send('都不队') gogogo.send('全都队') gogogo.send('一半队') ## 运行结果 接下来选手们将依次出发 都不队出发啦! 全都队出发啦! 一半队出发啦!
生成器表达式
除了利用函数和yield构造生成器,还可以用生成器表达式来构造。
l1 = (表达式 for 变量 in 可迭代对象) # 生成器对象
l1 = (f'伞兵{i}号准备就绪' for i in range(1, 6))
print(l1) # <generator object <genexpr> at 0x00000266EBA956D0>
for i in l1:
print(i)
## 运行结果
伞兵1号准备就绪
伞兵2号准备就绪
伞兵3号准备就绪
伞兵4号准备就绪
伞兵5号准备就绪
生成器的概念可以理解为分段的函数,你调用一次next方法就执行一次,生成器表达式虽然是一个式子,但本质上只是定义函数+yield的简化版本。
我们可以通过以下例子来深入理解一下。
进阶面试题
def test(): # 生成器
for i in range(4):
yield i
g = test() # 激活生成器
for n in [1, 10]:
g = (n + i for i in g)
res = list(g)
print(res)
# A. res=[10,11,12,13]
# B. res=[11,12,13,14]
# C. res=[20,21,22,23]
# D. res=[21,22,23,24]
"""
第一次for循环
g = (n+i for i in g)
第二次for循环
g = (n+i for i in ((n+i) for i in g))
"""
我们将第二次for循环的表达式展开为函数就是:
n = 10 # 在第二次for循环状态时,n = 10,循环结束后全局变量n定格在10了
def func():
def func1():
def test():
for i in range(4):
yield i # 0,1,2,3的
res1 = test()
for i in res1:
yield n + i # i代入,n=10代入,输出10+i
res2 = func1()
for i in res2: # 10,11,12,13
yield n + i # i代入,n=10代入,逐个输出,20,21,22,23
res = func()
for i in res:
print(i)
所以将生成器转换为列表得到答案C