迭代器与生成器

titledatetagscategoriesdescription
迭代器与生成器
2019-09-23 12:25:39 -0700
python基础
python
python中的迭代器与生成器

迭代器

迭代是访问集合元素的一种方式,迭代器是一种可以记住遍历的位置的对象,从集合的第一个元素开始访问知道所有的元素访问完结束

查看是否可以迭代

  • 可迭代的对象可以使用isinstance验证与Iterable的关系,返回True则可以迭代
  • 迭代器可以使用isinstance验证与Iterator的关系,返回True则可以迭代
from collections import Iterable
print(isinstance([11, 22], Iterable))

定义可以迭代的类

  • 如果想让创建的对象可以迭代,必须实现__iter__()方法,且需要返回一个迭代器

返回的迭代器也是一个对象,这个对象要包含__iter__()方法和__next__(),方法,其中__next__()方法的返回值就是迭代的内容

可以重新写一个迭代器的类,用于返回值 可以直接返回self,并在本类中实现__next__()

  • 两个类实现迭代
class my_iter():
    def __init__(self):
        self.names = list()

    def my_iterator(self, name):
        self.names.append(name)

    def __iter__(self):
        # 返回一个迭代器对象
        return my_itor(self)


class my_itor():
    def __init__(self, obj):
        self.obj = obj
        self.current_num = 0

    def __iter__(self):
        pass

    def __next__(self):
        if self.current_num < len(self.obj.names):
            ret = self.obj.names[self.current_num]
            self.current_num += 1
            return ret
        else:
            # 超过容量抛出StopIteration来终止迭代
            raise StopIteration


my_iterator = my_iter()
my_iterator.my_iterator("张三")
my_iterator.my_iterator("李四")
my_iterator.my_iterator("王五")
for temp in my_iterator:
    print(temp)

运行结果

张三
李四
王五

实现了迭代功能

  • 一个类实现迭代
class my_iter():
    def __init__(self):
        self.names = list()
        self.current_num = 0

    def my_iterator(self, name):
        self.names.append(name)

    def __iter__(self):
        # 返回自身,会自动调用__next__()方法
        return self

    def __next__(self):
        if self.current_num < len(self.names):
            ret = self.names[self.current_num]
            self.current_num += 1
            return ret
        else:
            # 超过容量抛出StopIteration来终止迭代
            raise StopIteration


my_iterator = my_iter()
my_iterator.my_iterator("张三")
my_iterator.my_iterator("李四")
my_iterator.my_iterator("王五")
for temp in my_iterator:
    print(temp)

运行结果

张三
李四
王五

也实现了迭代功能

  • 迭代器在类型转换中的作用
my_list = [ 11, 22, 33 ]
my_tuple = tuple(my_list)
print(my_tuple)

运行结果

(11, 22, 33)

实质上是先生成一个空的元组,然后调用my_list中的迭代方法,向my_tuple中添加内容

iter()next()方法

  • iter()用于获取可迭代对象的迭代器,实质上是调用了可迭代对象的__iter__()方法
  • next()方法用于获取迭代对象的下一条数据,迭代完成后抛出StopIteration的异常终止迭代
>>> l1 = [11, 22, 33]
>>> l1_iter = iter(l1)
>>> next(l1_iter)
11
>>> next(l1_iter)
22
>>> next(l1_iter)
33
>>> next(l1_iter)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

生成器

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

创建生成器

方法一
  • 将列表生成式的[]换成()
>>> l = [x for x in range(5)]
>>> l
[0, 1, 2, 3, 4]
>>> g = (x for x in range(5))
>>> g
<generator object <genexpr> at 0x7f924b6088d0>
方法二
  • 使用yield创建
  • yield会返回它后面的值,并将程序停留于此,等待下一次调用从当前位置开始执行
  • 示例
# 输出斐波纳奇数列
def create_num(all_num):
    print("---1---")
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        print("---2---")
        # 如果一个函数中有yield语句,那么这个就不再是函数,而是一个生成器的模板
        yield a
        print("---3---")
        a, b = b, a+b
        current_num += 1
        print("---4---")


# 如果在调用create_num的时候,发现这个函数中有yield,那么此时不是调用函数,而是创建一个生成器对象
obj = create_num(5)

for num in obj:
    print(num)

运行结果

---1---
---2---
0
---3---
---4---
---2---
1
---3---
---4---
---2---
1
---3---
---4---
---2---
2
---3---
---4---
---2---
3
---3---
---4---

可见只输出了一次---1---,而当第二次循环的时候没有进行初始化,直接在while循环中的yield下面继续执行,即yield返回后面的a后,停留在当前位置,等待下一次执行

另外,当使用同一个模板创建了多个生成器时,相互之间互不影响

  • 当生成器执行完成后有返回值时,可以使用异常获取
def create_num(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        yield a
        a, b = b, a+b
        current_num += 1
    return "遍历结束"


obj = create_num(5)
while True:
    try:
        # 使用next()启用生成器
        ret = next(obj)
        print(ret)
    except Exception as e:
        # 获取返回值
        print(e.value)
        break

运行结果

0
1
1
2
3
遍历结束

启动生成器

使用next()启动

上面的例子中都是使用next()启动的,这里不再介绍

使用send()启动
  • send()中可以填入参数作为yield执行后的返回值
def create_num(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
        res = yield a
        print(res)
        a, b = b, a+b
        current_num += 1
    return "遍历结束"


obj = create_num(5)
ret = next(obj)
print(ret)
while True:
    try:
        ret = obj.send("你好")
        print(ret)
    except Exception as e:
        print(e.value)
        break

运行结果

0
你好
1
你好
1
你好
2
你好
3
你好
遍历结束

可见,send()"你好"作为参数传递给了yield执行后的结果res,并且,它是下一次进入函数才传递的,这点可以从结果中先输出0(print(ret)),后输出你好(print(res))可以看出来 因此,send()如果作为第一次启动生成器时,必须使用obj.send(None),即不传入参数,或使用next(obj)来第一次启动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值