迭代 迭代器 生成器

学习协程之前先来学习几个知识点:
迭代

使用for循环遍历取值的过程叫做迭代,如使用for循环遍历列表获取值的过程

可迭代对象:使用for循环遍历取值的对象叫做可迭代对象, 比如:列表、元组、字典、集合、range、字符串

判断对象是否是可迭代对象

    from collentions import Iterable

    result = isinstance((3, 5), Iterable)  # 返回True

那么如何自己来定义一个可迭代对象?

自定义可迭代对象:在类里面定义__iter__() 方法创建的对象就是可迭代对象

但是遍历可迭代对象依次获取值需要迭代器。

在类里定义__iter__()方法创建的对象就是可迭代对象,可迭代对象 需要迭代器完成取数据的操作。

可迭代对象的本质是通过迭代器完成取值操作的。

可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据

迭代器 

    在类里定义__iter__()和__next__()方法创建的对象就是迭代器对象 from collections import Iterator

from collections import Iterable
from collections import Iterator


# 自定义可迭代对象
class MyIterable(object):

    def __init__(self):
        # 创建列表,存储添加的数据
        self.my_list = list()

    # 添加数据
    def append_item(self, item):
        self.my_list.append(item)

    def __iter__(self):
        # 可迭代对象的本质是通过迭代器完成取值操作的
        # 这里需要返回一个迭代器
        my_iterator = MyIterator(self.my_list)
        # 判断对象是否是迭代器对象
        #result = isinstance(my_iterator, Iterator)
        #print("my_iterator是否是迭代器类型对象:", result)
        return my_iterator


# 自定义迭代器对象: 在自定义类里面提供一个__iter__和一个__next__的方法创建的对象就是自定义迭代器对象
# 迭代器是可以完成取值操作的
class MyIterator(object):

    def __init__(self, my_list):
        self.my_list = my_list
        # 记录取值的下标
        self.current_index = 0

    # 需要返回一个迭代器
    def __iter__(self):
        #返回自身
        return self

    # 通过next方法获取迭代器中的下一个值
    def __next__(self):

        if self.current_index < len(self.my_list):
            value = self.my_list[self.current_index]
            # 对下标加1
            self.current_index += 1
            return value
        else:
            # 下标越界,需要抛出停止迭代异常,表示取值完成
            raise StopIteration

# 创建自定义可迭代对象
my_iterable = MyIterable()
# 添加数据
my_iterable.append_item(1)
my_iterable.append_item(2)

# 判断对象是否是可迭代对象
result = isinstance(my_iterable, Iterable)
print("my_iterable是否是可迭代类型对象:", result)
# 遍历可迭代对象,依次获取添加的数据
for value in my_iterable:
    print(value)

 iter()函数与next()函数

  1. iter函数: 获取可迭代对象的迭代器,会调用可迭代对象身上的__iter__方法
  2. next函数: 获取迭代器中下一个值,会调用迭代器对象身上的__next__方法

for循环的本质

    遍历的是可迭代对象

  • for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。

next(iter(Iterable)) 可以取值

    遍历的是迭代器

  • for item in Iterator 循环的迭代器,不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。

next(Iterator)可以取值

# 自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
class MyList(object):

    def __init__(self):
        self.my_list = list()

    def append_item(self, item):
        self.my_list.append(item)

    def __iter__(self):
        my_iterator = MyIterator(self.my_list)
        return my_iterator


# 迭代器是记录当前数据的位置以便获取下一个位置的值
class MyIterator(object):

    def __init__(self, my_list):
        self.my_list = my_list
        self.current_index = 0

    def __iter__(self):
        return self

    # 获取迭代器中下一个值
    def __next__(self):
        if self.current_index < len(self.my_list):
            self.current_index += 1
            return self.my_list[self.current_index - 1]
        else:
            # 数据取完了,需要抛出一个停止迭代的异常
            raise StopIteration

# 创建了一个自定义的可迭代对象
my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)

# 获取可迭代对象的迭代器
my_iterator = iter(my_list)
print(my_iterator)
# 获取迭代器中下一个值
# value = next(my_iterator)
# print(value)

# 循环通过迭代器获取数据
while True:
    try:
        value = next(my_iterator)
        print(value)
    except StopIteration as e:
        break

迭代器好处: 

  1. 节省内存,每次根据算法只会生成一个数据,也就是说只占用一个变量的内存空间
  2. 不会受到生成数列个数的影响

demo 迭代器完成斐波那契数列

# 自定义迭代器
class Fibonacci(object):

    def __init__(self, num):
        # num: 表示创建斐波那契数列的个数
        self.num = num
        # 记录数列的前面两个值
        self.first_value = 0
        self.second_value = 1
        # 记录生成个数的下标
        self.current_index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current_index < self.num:
            # 临时存储第一个变量的值
            result = self.first_value
            # 根据算法生成对应的数列的值
            self.first_value, self.second_value = self.second_value, self.first_value + self.second_value
            self.current_index += 1
            return result
        else:
            raise StopIteration

# 创建迭代器
fib = Fibonacci(5)
# value = next(fib)
#
# print(value)
#
# value = next(fib)
#
# print(value)

for value in fib:
    print(value)

生成器

生成器是一类特殊的迭代器,它不需要再像上面的类一样定义__iter__和__next__方法,但它依然可以使用next函数和for 循环取值。

创建生成器的方法:

  • 将列表生成式的[]改成()
  • 在def函数里看到有yield关键字那么就是生成器

在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。

 demo 生成器完成斐波那契数列

def fibonacci(num):
    first_value = 0
    second_value = 1
    # 记录生成个数的下标
    current_index = 0
    print("----111----")
    while current_index < num:
        result = first_value
        first_value, second_value = second_value, first_value + second_value
        current_index += 1
        print("----222----")
        # 当代码执行到yield关键字的时候代码会暂停,然后结果返回给外界,再次启动生成器会在暂停的位置继续往下执行
        yield result
        print("----333----")

generator = fibonacci(2)
print(generator)

#value = next(generator)
#print(value)
#value = next(generator)
#print(value)

for value in generator:
    print(item)

使用send方法启动生成器并传参

send的好处是可以让用户传入参数,使生成器根据用户输入的参数不同,做出不同操作。

第一次使用send启动生成器时只能传入None,所以推荐第一次启动生成器使用next。

def fibonacci(num):
    # num: 表示使用生成创建斐波那契数列的个数
    # 记录数列前面两个值
    first_value = 0
    second_value = 1
    # 记录生成个数的下标
    current_index = 0
    print("----111----")
    while current_index < num:
        result = first_value
        first_value, second_value = second_value, first_value + second_value
        current_index += 1
        print("----222----")
        # 当代码执行到yield关键字的时候代码会暂停,然后结果返回给外界,再次启动生成器会在暂停的位置继续往下执行
        # 第二次启动生成器的时候传入的参数赋值给params变量
        params = yield result
        print(params)
        if params == "ok":
            return
        print("----333----")

fib = fibonacci(5)

value = next(fib)
print(value)

# send方法启动传入参数给生成器
value = fib.send("传入的参数")
print(value)


value = fib.send("传入的参数")
print(value)

# value = fib.send("ok")  # 抛出StopIteration的错误
# print(value)

----111----
----222----
0
传入的参数
----333----
----222----
1
传入的参数
----333----
----222----

  • 生成器创建有两种方式,一般都使用yield关键字方法创建生成器
  • yield特点是代码执行到yield会暂停,把结果返回出去,再次启动生成器在暂停的位置继续往下执行

猜你喜欢

转载自blog.csdn.net/w18306890492/article/details/82926339