迭代器,生成器

可迭代对象

【1】什么叫迭代?

迭代其实就是更新换代,每一次迭代的过程都需要依赖上一次的结果

例如

# 属于迭代,每次结果都依据上一次结果
# 例一
n = 1
while True:
    print(n)
    n +=1
 
# 例二    
l1 = [1,2,3,4,5,6,7,8,9]
n = 0
while n < len(l1):
    print(l1[n])
    n += 1

【2】什么是可迭代对象

1.内置有__iter__方法的都可以称为可迭代对象
2.__iter__该类型代码的读法>>>:双下iter方法

【3】哪些数据是可迭代对象

依次列举并尝试调用双下iter方法即可

可迭代(支持for循环):字符串(str),字典(dict),元组(tuple),列表(list),集合(set)

不可迭代:整型(int), 浮点型(float),布尔型(bool)

迭代器对象

【1】什么是迭代器对象

可迭代对象调用双下iter方法之后生成的结果就是迭代器对象

【2】迭代器对象的特征

含有双下iter方法又含有双下next方法

含有__iter__又含有__next__
s=[1,2,3,4,5,6,7,8,9,10]
res = s.__iter__()
print(res) # <list_iterator object at 0x000001BAE2829780>
'''
迭代器对象 iterator object
'''
pice = res.__next__()
print(pice)
# 1       输出一次 依次弹出列表中的一个元素

【3】如何理解迭代器对象

迭代器对象能够极大的节省存储空间

  • eq:类似于哆啦A梦的口袋,不用就是一个口袋面积,用的时候可以从中取出很多数据

【4】迭代器对象如何取值

调用双下next方法即可,如果取完了就会报错

【5】扩充了解

1.有很多双下方法其实都有简便写法,但不是全部

'''
__方法名__ 等价  方法名()
       最常见的两个是
         __iter__   iter()
         __next__   next()
'''

2.有一些可迭代对象本身也是迭代器对象:>>> 文件对象

3.可迭代对象调用一次双下iter方法编程迭代器对象,如果继续调用,结果还是迭代器对象本身

for循环内部原理

'''
for循环底层原理
    for 变量名 in 可迭代对象:
        循环体代码
1.会将in后面的数据调用__iter__()变成迭代器对象
所以就解释了为什么文件对象也可以for循环,因为本身就是迭代器对象,再次调用不变
2.针对产生的迭代器对象依次调用__next__()方法迭代取值
3.当取值完之后,会自动处理报错并退出循环
'''
user_dict = {'name':'silence','age':18,'password':123}
# 1.先转换成迭代器对象
# res= user_dict.__iter__()
res = iter(user_dict)
# 2. 迭代取值
while True:
    # print(res.__next__())
    try:
        print(next(res))
    except Exception as e:
        # 结束while循环
        break
        
# name
# age
# password

迭代取值与索引取值的对比

1.索引取值

1.优势:

  • 可以反复获取相同元素,并没有固定的方向

2.劣势:

  • 只能支持有序的容器类型,无序的无法取值兼容性没有迭代取值高

2.迭代取值

1.优势:

  • 兼容所有的容器类型
  • 不使用索引进行取值
  • 字典是不支持索引取值的
  • 取到这个值的时候就会保存到当前的状态,下一次从这个位置开始向下取值

2.劣势:

  • 取值的顺序永远都是从左往右,并且无法重复获取,取完就完了
  • 除非取值取到尽头,否则永远不知道终止索引在哪里
  • 迭代同一个对象只能重新创建
  • 调用 next 取值的前提是 已经生成并且得到了一个迭代器对象

生成器对象(自定义迭代器)

def index():
    print('你还记得我嘛')
    # yield 123
    yield 123,111
# 没有调用之前就是一个普通的函数
'''
当函数体代码中含有yield关键字
    第一次调用函数并不会执行函数体代码
    而是将函数变成了生成器
'''
# print(index) # <function index at 0x000001FF8B493E20>
# 加括号调用并接收结果:不执行代码,而是变成生成器对象(迭代器)
res = index()
# print(res)  #<generator object index at 0x00000204F3AE6960> 变成生成器对象
# 变成生成器之后调用__next__就会开始执行函数体代码
# print(next(res)) #123 yield有点像return功能
print(next(res))  #(123, 111)
'''
生成器对象也是节省存储空间的 特性与迭代器对象一致
'''
def index():
    print('你还记得我嘛')
    yield 123
    print('是不是忘记我了')
    yield 123,111
'''
如果函数体代码中含有多个yield关键字,每执行一次__next__可以返回后面的值,并且让代码停留在yield位置
再次执行__next__基于上次的位置往后执行到下一个yield关键字处
如果没有了,再执行就会报错 StopIteration
'''
res = index()

print(next(res)) # 123
print(next(res)) # (123,111)
print(next(res))  # 报错
# 用for循环也可以 直接for自带报错处理
def index():
   
    yield 123
    
    yield 123,111

res = index()
for i in res: 
    print(i)
#123
#(123, 111)

自定义range方法

range方法其实是一个可迭代对象


'''
需求:通过生成器模拟range方法
for i in range(1,10):
    print(i)
'''
# 先以两个参数的range参数为例
def my_range(start, end):
    while start < end:
        yield start
        start += 1
for i in my_range(1,10):
    print(i)
# 初步实现之后 在考虑不同参数情况 一个参数 三个参数
# 先针对一个
'''
end可以不传值 应该设置成默认参数 end=None
代码层面做判断 将形参数据做替换处理
        end=start
        start=0 
'''
def my_range(start, end=None):
    if not end:
        end = start
        start = 0
    while start < end:
        yield start
        start += 1
for i in my_range(10):
    print(i)
    
# 针对三个参数情况
'''
给函数添加第三个形参 并且设置成默认参数 默认值是1 step=1
每次递增只需要递增step的数值即可
'''


def my_range(start, end=None, step=1):
    if not end:
        end = start
        start = 0
    while start < end:
        yield start
        start += step


for i in my_range(1, 10, 2): 
    print(i)# 1,3,5,7,9
for i in my_range(10):
    print(i)# 0,1,2,3,4,5,6,7,8,9
for i in my_range(1,10):
    print(i) # 1,2,3,4,5,6,7,8,9

yield关键字作用

1.在函数体代码中出现 可以将函数变成生成器

2.在执行过程中,可以将后面的值返回出去,类似于return

3.还可以暂停住代码的运行

4.还可以接收外界的传值

yield接收外界传值 用语法.send()

def eat(name):
    print(f'{name} 准备干饭')
    while True:
        food = yield
        print(f'{name} 正在吃{food}')
res = eat('大胃王')
# 想执行一次,如果想执行多次直至结束 可以直接用for循环
res.__next__()
# yield 可以接收外部传值
res.send('鱼香肉丝')  # 可以给yield传值,并且自动调用一次__next__方法
res.send('宫保鸡丁')
# 大胃王 准备干饭
# 大胃王 正在吃鱼香肉丝
# 大胃王 正在吃宫保鸡丁

生成器表达式

也是为了节省存储空间

重点:

生成器内部的代码只有在调用双下next迭代取值的时候才会执行

面试题示例

# 普通求和函数
def add(n,i):
    return n+i

# 生成器对象 返回 1,2,3
def test():
    for i in range(4):
        yield i
# 将test函数变成生成器对象
g=test()
# 简单for循环
for n in [1,10]:
    g = (add(n,i) for i in g)
    '''
    从上到下还没有调用函数 只有运行两次for循环,只有对g进行迭代取值是才进行代码操作
    第一次for循环
     g = (add(n,i) for i in g)
     第二次for循环
      g = (add(n,i) for i in  (add(n,i) for i in g))
      再list(g)调用函数时 第二次for循环里的n都为10,而i是0
      所以第一次调用的时候就是10+10  20是起始数
    '''
print(n)
res = list(g)# list底层就是for循环 相当于对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]
# 正确答案是c 诀窍就是抓n是多少即可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值