Python迭代器

迭代器


迭代 是访问集合元素的一种方式。迭代器 是一个可以记住遍历的位置的对象。迭代器 对象从集合的第一个元素开始访问,直到所有的元素被访问结束。迭代器 只能往前不会后退。

1. 可迭代对象

我们已经知道可以对 list、tuple、str 等类型的数据使用 for...in... 的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫 迭代

但是,是否所有的数据类型都可以放到 for...in... 的语句中,然后让 for...in... 每次从中取出一条数据提供我们使用,即供我们迭代吗?

In [1]: for i in 100:
   ...:     print(i)
   ...:
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-86150fa0c47d> in <module>
----> 1 for i in 100:
      2     print(i)
      3

TypeError: 'int' object is not iterable

# int 整型不是iterable,即int整型不是可以迭代的

# 我们自定义一个Classmate类用来存放数据,可以通过add方法向其中添加数据
class Classmate(object):
    def __init__(self):
        self.names = list()
    def add(self, name):
        self.names.append(name)
classmate = Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王五")
for name in classmate:
    print(name)
# 运行结果:
TypeError: 'Classmate' object is not iterable  

# Classmate() 对象也是不可以迭代的。

2. isinstance: 判断是否可以迭代?

In [1]: from collections import Iterable
In [2]: isinstance([11,22,33], Iterable)
Out[2]: True
# 只要返回值为True,就意味着可以迭代
In [3]: isinstance("abcdef", Iterable)
Out[3]: True

In [4]: isinstance((100,200,300), Iterable)
Out[4]: True

In [5]: isinstance(100, Iterable)
Out[5]: False

3. 迭代器

实际开发中,往往出现一种场景。创建出的对象 (classmate),这个对象里面有个属性(self.names),这个列表里面添加了很多东西,能不能用 for 去这个对象里面的值呢?可以!怎么做呢?

1. 在创建这个对象的类里面必须实现 __iter__ 方法

2. __iter__ 方法 必须返回一个迭代器的对象引用,什么是迭代器呢?只要这个类里面有__iter__ 方法和 __next__ 方法,就称这样的类创建出来的对象叫做迭代器,他的一大特点就是可以取里面的值。(返回的是哪一个对象的引用,接下来for循环在取值的时候实际上就去取这个对象里面的 __next__ 方法的返回值,__next__ 返回什么,接下来就当 for 循环的那个值是什么。)

4. 代码实现

import time

class Classmate(object):
    def __init__(self):
        self.names = list()

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

    def __iter__(self):
        """
        如果让一个对象成为一个可以迭代的对象,即可以使用for,
        那么必须实现__iter__方法
        """
        return ClassIterator(self)  
        """把这个对象(ClassIterator)的引用返回了,直白地讲就是得到了这个
        对象的引用,得到了这个对象的引用,实际上for循环就会调用它
        (ClassIterator)的__next__方法,调一次取一个,放到temp里面
        打印,每for一次都会调用一次 __next__ ,__next__ 返回什么,
        就给temp什么。"""
        """
        言外之意是我创建的是Classmate的对象,把这个对象的引用
        Classmate()放到for循环后面去了,但是在最终实现的过程中,
        你这个方法(__iter__)返回的是哪一个对象的引用,那么将来在for
        循环的整个过程中,它就调用返回的对象里面的 __next__ 方法,
        __next__ 返回什么,for就看见什么
        """

class ClassIterator(object):  # 迭代器
    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:
            raise StopIteration

classmate = Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王五")

for name in classmate:
    print(name)
    time.sleep(1)

 5. 代码升级

import time

class Classmate(object):
    def __init__(self):
        self.names = list()
        self.current_num = 0

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

    def __iter__(self):
        """如果让一个对象成为一个可以迭代的对象,即可以使用for,
        那么必须实现__iter__方法"""
        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就执行,raise StopIteration就意味着产生一
        个异常,产生一个异常因为并没有try来处理,产生异常自动的传递到
        for循环这个地方来处理,for循环自动的检测到StopIteration之后,
        停止了迭代"""
        else:
            raise StopIteration

classmate = Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王五")

for name in classmate:
    print(name)
    time.sleep(1)

6. for...in...本质 

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

怎么告诉 for 循环我已经取(迭代)完了呢?

通过产生一个自定义抛出一个异常 StopIteration,自定义抛出异常就解决了这个问题!

7. 迭代器的应用场景

举个例子,比如,数学中有个著名的斐波拉契数列(Fibonacci),数列中第一个数为0,第二个数为1,其后的每一个数都可由前两个数相加得到:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...

现在我们想要通过for...in...循环来遍历迭代斐波那契数列中的前n个数。那么这个斐波那契数列我们就可以用迭代器来实现,每次迭代都通过数学计算来生成下一个数。

class Fibonacci(object):
    def __init__(self, all_num):
        self.all_num = all_num
        self.current_num = 0
        self.a = 0
        self.b = 1

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

    def __iter__(self):
        """如果让一个对象成为一个可以迭代的对象,即可以使用for,那么必须实现__iter__方法"""
        return self

    def __next__(self):
        if self.current_num < self.all_num:
            ret = self.a
            self.a, self.b = self.b, self.a + self.b
            self.current_num += 1
            return ret
        else:
            raise StopIteration


fiber = Fibonacci(10)


for name in fiber:
    print(name)

8. 并不是只有for循环能接收可迭代对象

除了for循环能接收可迭代对象,list、tuple等也能接收。

li = list(FibIterator(15))
print(li)
tp = tuple(FibIterator(6))
print(tp)

元组 FibIterator(15),list 方法可以将他转成列表;

列表 FibIterator(6),也可以通过 tuple 方法转成 元组,到底是怎么做的呢?

并不是简单地转换类型,而是通过迭代的方式取里面的值,取一个生成一个。比如 

In [1]: a = (11,22,33)

In [2]: list(a)
Out[2]: [11, 22, 33]

它是怎么做的呢?先生成一个空列表,接下来调用 list(a) 里面的迭代器 ,因为 a 是一个迭代对象,他通过 __iter__ 函数找到了里面的迭代器,找到了迭代器之后通过 __next__ 取里面的值,就完成了。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值