迭代器、生成器

迭代器

【一】迭代器介绍

  • 迭代器就是用来迭代取值的工具,是重复反馈过程的程序
  • 目的是为了更加逼近我们想要的目标和结果
  • 每一次迭代得到的返回结果就是下一次迭代开始的初始值
num_list = [1,2,3,4,5,6]
count = 0
while count <len(num_list):
    # 每一次使用的索引位置就是上一次+1后的索引位置
    print(num_list[count])
    count += 1
    
1
2
3
4
5
6

【二】可迭代对象

【1】什么是可迭代对象

  • 可以通过列表的索引进行取值,但是如果一个数据没有索引的话
  • 必须找到一种可以用索引也可以不用索引的方法来取值

【2】八大基本数据类型

补充

生成可迭代对象的两种方式

iter()

相当于

__iter__(数据)

迭代器对象迭代的方法的两种方式

迭代器对象.__next__()

next(迭代器对象)

数字类型

整数
age = 18
print(iter(age))
print(age.__iter__)
报错 	'int' object is not iterable

浮点数
age = 18.5
print(iter(age))
print(age.__iter__)
报错	'float' object is not iterable

字符串类型

qwer = 'qewr'
print(iter(qwer))
print(qwer.__iter__)

输出
<str_iterator object at 0x0000021D016BA740>
<method-wrapper '__iter__' of str object at 0x0000021D0165D3B0>

列表类型

list = [1,2,3,4,5,6]
print(iter(list))
print(list.__iter__)

输出
<list_iterator object at 0x000001FA4D7097B0>
<method-wrapper '__iter__' of list object at 0x000001FA4D6B4E40>

字典类型

dict = {'q':'1','w':2}

print(iter(dict))
print(dict.__iter__)

输出
<dict_keyiterator object at 0x000001E457AED670>
<method-wrapper '__init__' of dict object at 0x000001E457AA1F40>

布尔类型

bool = True
print(iter(bool))
print(bool.__iter__)

报错 	'bool' object is not iterable

元组类型

tuple = (1,2,3,4,5,6)

print(iter(tuple))
print(tuple.__iter__)

输出
<tuple_iterator object at 0x000001AE3656A740>
<method-wrapper '__init__' of tuple object at 0x000001AE36578940>

集合类型

set = {1,2,3,4,5,6}
print(iter(set))
print(set.__iter__)

输出
<set_iterator object at 0x000001E74BFF2080>
<method-wrapper '__init__' of set object at 0x000001E74C026DC0>

【3】总结

能.__iter__不报错的就是可迭代类型 ————>可迭代对象

可迭代类型

字符串、列表、元组、集合、字典

不可迭代类型

整数、浮点数、布尔

【三】迭代器对象

【1】什么是迭代器对象

# 如果调用obj.__iter__()返回的是一个可迭代对象

# 有的可迭代对象还有__next__()方法
# 迭代器对象实在可迭代对象(具有.__iter__()方法)的基础上具有.__next__()方法的对象

【2】八大基本数据类型(除了不可迭代)

(1)字符串

name = "qwer"
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__()) # 会报错 most recent call last

<str_iterator object at 0x00000202DE74A740>
q
w
e
r
第五行报错

(2)列表

name = [1,2,3,4,5,6]
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())


<list_iterator object at 0x0000026B66C3A740>
1
2
3
4
5

(3)字典

得到的是键

name = {'q':1,'w':'2','e':'3','r':'4'}
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())


<dict_keyiterator object at 0x000001C4B92DD670>
q
w
e
r

(4)元组

name = (1,2,3)
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())


<tuple_iterator object at 0x000001D9C593A740>
1
2
3

(5)集合

name = {1,2,3,4,5,6}
name_iter = name.__iter__()
print(name_iter)
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())
print(name_iter.__next__())

<set_iterator object at 0x00000234968D2000>
1
2
3
4

【3】总结

具有__iter__() 和 __next__()方法的对象是可迭代对象
  • 在八大基本数据类型中,除了数字和布尔类型,其他都是迭代器对象

  • 迭代器对象一定是可迭代对象,可迭代对象不一定是迭代器对象

  • 迭代器对象是同时具有上述两个方法的对象

  • 可迭代对象只需有第一个方法既是可迭代对象

【四】迭代器的优缺点

  1. 优点
    • 不使用索引取值(例如字典不支持索引取值)
    • 取到值后会保存当前状态,下次从当前位置开始继续取值
  2. 缺点
    • 除非取值取到尽头,否则永远不知道终止索引在哪里
    • 只能取一次记一次位置,下一次的其实位置是上一次的终止位置,想回到开头 回不去
    • 调用 next 取值的前提是 已经生成并且得到了一个迭代器对象 iter_obj
    • 迭代同一个对象只能重新创建
name = 'dr'
name_iter = name.__iter__()
print(next(name_iter))
print(next(name_iter))
name_iter = name.__iter__()	# 要重复取值必须重新创建迭代器对象
print(next(name_iter))

d
r
d 

生成器

【一】什么是生成器

  • 生成器是一种特殊的迭代器

  • 在需要的时候给你数据,不需要就不会给数据

  • for循环 range(10) 可以把生成的数字取出来

  • 数据量过大的话 读取数据内存满,电脑卡死

  • 生成器 ---> 一次取...行 --->处理完这些数据后--->再取...行

例子 for i in fp:

【二】生成器的创建方式

【1】列表推导式

print([i for i in range(10)])
# 如果将列表生成式外面的列表换成元组,则会生成一个生成器对象
print((i for i in range(10)))

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
<generator object <genexpr> at 0x0000020E358F8430>

【2】yiled关键字

def add(i):
    yield i

# add(2)进入到add函数,将原本的函数对象转换为生成器对象
res = add(2)
print(res)
# 想取值只能使用next来迭代取值
print(res.__next__())

<generator object add at 0x00000155DB1E8430>
2
def add(i):
    # i = 1
    while True:
        yield i	 # 第一次next


res = add(1)
print(res)
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
res = add(3)
print(res.__next__())


<generator object add at 0x000001EFDF0CEAB0>
1
1
1
1
3

补充

在第一次调用生成器时,`yield`语句会在赋值操作符`=`的右边停下。

考虑下面的代码:

```python
def my_generator():
    x = yield
    print("Received:", x)

gen = my_generator()
```

当你执行`gen = my_generator()`时,生成器`my_generator`并没有开始执行。相反,它返回了一个生成器对象`gen`。此时,`my_generator`函数中的代码并没有运行。

然后,当你调用`gen.__next__()`或`next(gen)`时,生成器才会开始执行。它会执行到`x = yield`这一行,但暂停在`yield`右边,等待接收一个值。这个值会被赋给`x`。因此,第一次调用`yield`语句时,生成器会停在赋值操作符`=`的右边。

【三】生成器案例

def outer():
    print("开始吃饭")
    while True:
        food = yield  # 等同于yield(food)
        print(f"做了{food},吃{food}")

res = outer()
print(res)
print(res.__next__())
print(res.__next__())


<generator object outer at 0x00000183F2538430>
开始吃饭
None	
做了None,吃None
None	# 原因是在生成器内部走完了上面代码,又回到了yield 没有返回值

yield语句在生成器函数中充当了暂停和恢复执行的标记,同时也充当了返回值的功能。当生成器函数执行到yield语句时,它会暂停执行并将yield后面的表达式作为返回值返回给调用方。在上述代码中food为yield的后面的表达式

向生成器中传值

send() 传值后会再往下走一步

res = outer()
print(res)
print(res.__next__())
res.send('炒饼') # 向 yield 传值并且让生成器向下走一下
print(res.__next__())
res.send('炒饭')


<generator object outer at 0x000002A8C99E8430>
开始吃饭
None
做了炒饼,吃炒饼
做了None,吃None
None
做了炒饭,吃炒饭

【四】装饰器 + 生成器

def init_iter(func):
    def inner(*args,**kwargs):
        # g 是得到的生成器对象
        g = func(*args,**kwargs)
        # 调用自己生成器往下走
        next(g)
        # 走回来的返回值返回出去
        return g
    return inner

@init_iter
def outer():
    print("开始吃饭")
    while True:
        # yield food
        food = yield
        print(f"做了{food},吃{food}")

res = outer()
res.send('炒米粉')
res.send('炒fan')


开始吃饭
做了炒米粉,吃炒米粉
做了炒fan,吃炒fan

【五】生成器内部修改可变数据类型

def init_iter(func):
    def inner(*args,**kwargs):
        # g 是得到的生成器对象
        g = func(*args,**kwargs)
        # 调用自己生成器往下走
        next(g)
        # 走回来的返回值返回出去
        return g
    return inner

@init_iter
def outer():
    print("开始吃饭")
    list = []
    while True:
        # yield food
        food = yield
        list.append(food)	因为没有退出循环,所以会把新值一直加到列表中
        print(f"做了{food},吃{food}")
        print(list)

res = outer()
res.send('炒米粉')
res.send('炒fan')


开始吃饭
做了炒米粉,吃炒米粉
['炒米粉']
做了炒fan,吃炒fan
['炒米粉', '炒fan']

【六】yield + next 搭配着使用

仿照循环自己构建循环函数

for i in range(5):
    print(i)
def my_range(start, stop, step):
    '''

    :param start: 0
    :param stop: 5
    :param step: 1
    :return:
    '''
    print("start")
    while start < stop:
        yield start
        start += step
    print("end")


res = my_range(0, 5, 1)
print(res)  # <generator object my_range at 0x000002DF0361CBA0>
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
print(res.__next__())
# 这里会报错 ---> 没有可迭代的值可以被获取了
print(res.__next__())  # StopIteration
def init_iter(func):
    def inner(*args, **kwargs):
        try:
            generator = func(*args, **kwargs)
            next(generator)
            return generator
        except StopIteration:
            pass

    return inner

def range_(start, stop, step):
    while start < stop:
        yield start
        start += step

# for 循环内部做了异常捕获
for i in range_(0,5,1):
    print(i)

def my_range(start, stop, step):
    def range_(start, stop, step):
        while start < stop:
            yield start
            start += step

    res = range_(start, stop, step)
    while True:
        try:
            print(res.__next__())
        except StopIteration:
            break

res = my_range(start=1, stop=5, step=1)
res

0
1
2
3
4
1
2
3
4
posted @   随机昵称yi  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示