你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

6-迭代器+生成器

2021/12/7 17:40:19

文章目录

    • 迭代器
      • 1. 可迭代对象
      • 2. 迭代器
      • 3. 动手写一个可迭代的类
      • 4. 封装到一个类中
      • 5. 迭代器的应用
        • range和xrange
        • 使用迭代器生成斐波那契数列
        • 列表解析式
    • 生成器
      • 生成器的函数实现
      • 通过send方式启动生成器

迭代器

1. 可迭代对象

from collections import Interable 
print(isinstance([11,22,33],Interable))

如果要使一个对象是可迭代的,必须重写__iter__方法,这样才能通过Interable的检验,但是此时仍不能通过for函数遍历,所以仅仅是可迭代对象,而不是迭代器。

示例代码:

from collections.abc import Iterable

class ClassMate():
    def __init__(self) -> None:
        self.name = list()
    
    def add(self,name):
        self.name.append(name)
    
    def __iter__(self):
        pass

classmate = ClassMate()
print("判断ClassMate是否是可迭代对象:",isinstance(classmate,Iterable))

2. 迭代器

迭代器必须重写__iter__方法和__next__方法,其中,__iter__方法必须返回一个对象的引用。

可以使用for循环遍历迭代器,for temp in xxx_obj的执行过程:

  1. 判断对象xxx_obj是否有__iter__方法(可迭代对象)
  2. 在第一步成立的条件下,调用iter函数,得到对象xxx_obj__next__的返回值,其返回值就是一个迭代器
  3. 自动调用迭代器的next函数,即调用迭代器的__next__方法,直到抛出StopIteration异常。

对于这样一个for循环

for i in seq:
    do_something_to(i)

实际上是这样工作的

fetch_iterator = iter(q)
while True:
    try:
        i = fetch_iterator.next()
    except StopIteration:
        break
    do_something_to(i)

迭代器非常重要,python3中字典、文件、列表都是可迭代的对象。

3. 动手写一个可迭代的类

from collections.abc import Iterable
from collections.abc import Iterator
# 实现将一个可迭代的类

class ClassMate(object):
    def __init__(self) -> None:
        self.names = list()

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

    def __iter__(self):
        # 返回一个迭代器的实例对象
        return ClassIterator(self)

class ClassIterator(object):
    def __init__(self,obj):
        self.obj = obj
        # 通过实例属性记录前一次指针的位置
        self.current_num = 0

    def __iter__(self):
        pass
    def __next__(self):
        """next方法执行此方法"""
        if self.current_num < len(self.obj.names):
            # 手动防止越界
            ret =  self.obj.names[self.current_num]
            self.current_num += 1
            return ret
        else:
            raise StopIteration
            # for 循环会自动捕获错误

    
classmate = ClassMate()
classmate.add('小王')
classmate.add('小美')
classmate.add('小明')

class_iterator = iter(classmate)


print("判断classmate是否是可迭代对象",isinstance(classmate,Iterable))
print("判断class_iterator是否是迭代器",isinstance(class_iterator,Iterator))

for name in classmate:
    print(name)

# Output:
# 判断classmate是否是可迭代对象 True
# 判断class_iterator是否是迭代器 True
# 小王
# 小美
# 小明

执行过程:
在这里插入图片描述

注意:

  1. 可迭代对象A的__iter__返回一个迭代器的 实例对象B, 迭代器B初始化时需要将 可迭代对象A 传入 初始化方法;
  2. 迭代器实例对象通过一个 实例属性self.current_num记录当前读取的位置
  3. 需要手动防止越界,否则for语句会不断执行__next__,并捕获返回的None,此时,可通过抛出 StopIteration 错误,for循环会自动捕获。

4. 封装到一个类中

上面的方法使用了两个类,有点浪费资源,我们将其全部封装到一个类中

from collections.abc import Iterable
from collections.abc import Iterator
# 实现将一个可迭代的类

class ClassMate(object):
    def __init__(self):
        self.names = list()
        self.current_count = 0

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

    def __iter__(self):
        # 返回自身实例对象
        return self
    
    def __next__(self):
        if self.current_count<len(self.names):
            ret = self.names[self.current_count]
            self.current_count+=1
            return ret
        else:
            raise StopIteration
    
classmate = ClassMate()
classmate.add('小王')
classmate.add('小美')
classmate.add('小明')

class_iterator = iter(classmate)


print("判断classmate是否是可迭代对象",isinstance(classmate,Iterable))
print("判断class_iterator是否是迭代器",isinstance(class_iterator,Iterator))

for name in classmate:
    print(name)

# Output:
# 判断classmate是否是可迭代对象 True
# 判断class_iterator是否是迭代器 True
# 小王
# 小美
# 小明

5. 迭代器的应用

range和xrange

【补充】rangexrange的区别:

range和xrange的区别:在python2中range返回数值,xrange返回一个可迭代的对象,占用了极小的空间,

在python3中,range也返回的是一个可迭代对象了。

使用迭代器生成斐波那契数列

例如可以使用迭代器实现斐波那契数列

# 一般实现
a = 0
b = 1
i = 0
while i<10:
    a,b = b,a+b
    print(a)
# 迭代器实现


class  Fibonacci(object):

    def __init__(self,all_num):
        self.max_num = all_num
        self.count_num = 0
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.count_num < self.max_num:
            self.count_num += 1
            self.a,self.b = self.b,self.a+self.b
            return self.a
        else:
            raise StopIteration

fibo = Fibonacci(10)
for num in fibo:
    print(num)

列表解析式

列表解析,List comprehensinos,来自于Haskell语言,可以取代lambda、map和filter函数的部分使用场景。

语法[expr for iter_val in iterable if cond_expr]

例1:计算一个序列成员的平方

map(lambda x:x**2,range(6))
# 或者
[x**2 for x in range(6)]

例2:挑选出序列seq中的奇数

filter(lambda x: x%2,seq)
# or
[x for x in seq if x%2]

例3:计算文本中的单词个数【列表解析式支持多重循环和多个if语句,执行顺序从左到右】

f = open('test.txt','r')
len([word for line in f for word in line.split()])

生成器表达式和列表解析式很类似:(expr for iter_val in iterable if cond_expr),因为生成器表达式使用了“延迟计算”(lazy evaluation),所以在内存上比列表解析式更加有效。

生成器

生成器的函数实现

通过send方式启动生成器