可迭代对象、迭代器对象、生成器对象

可迭代对象、迭代器对象、生成器对象

可迭代对象

可迭代对象都有一个特点,对象内置__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
posted @ 2022-10-14 18:45  leethon  阅读(72)  评论(0编辑  收藏  举报