Python迭代器的实现

迭代器:

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

1.可迭代对象:

如果我们想访问列表、元组、集合、字典、字符串等类型里面的数据,可以使用for…in… 循环依次访问。这个过程叫做遍历,也叫迭代。我们把列表、元组、集合、字典、字符串等这种可以使用for循环进行遍历的对象称为可迭代对象。
但是并不是所有的数据类型都可以放到 for…in… 的语句中然后取出里面的数据。看下面的一个例子:

for i in 10:	# 10是一个数字类型, 即 int类型
	print(i)

结果它会给我们报错,说 int 类型不是一个可以迭代的对象。也就是说 int 用不了for循环。

TypeError: 'int' object is not iterable

那问题来了:什么样的类型能用for循环?什么样的又不能用for循环?
如果说 in 后面的是一个可以迭代的对象,那就能用for循环,否则用不了。像数字类型、带小数点的都用不了。
问题又来了: 怎么判断一个对象是不是可以迭代的?
我们先导入一个模块

from collections import Iterable

Iterable 是一个可迭代的类,我们想要判断某一个东西是否是可以迭代的,只需要判断这个东西是不是 Iterable 的一个子类。我们通过isinstance(对象, 类) , 它的特点是可以判断某一个东西是否是某一个东西创建出来的。例如:

print(isinstance(list(), Iterable))     # 列表
print(isinstance(tuple(), Iterable))    # 元组
print(isinstance(set(), Iterable))      # 集合
print(isinstance(dict(), Iterable))     # 字典
print(isinstance(str(), Iterable))      # 字符串

---->结果<----
True
True
True
True
True

他们的返回结果是True,说明他们都是可以迭代的对象。那我们再来看下面的例子:

print(isinstance(int, Iterable))     # 数字
print(isinstance(float, Iterable))  # 浮点数

---->结果<----
False
False

返回的结果是 False, 说明他俩和 Iterable 没有关系, 他们不是可以迭代的对象。

2.自己定义一个类来实现可迭代

我们首先来定义一个类:

class AddName(object):

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

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

name = AddName()
name.add("老大")
name.add("老二")
name.add("老三")

定义了一个Name类, 并实例化一个对象,这个类里面有个列表,我往列表里里面放了三个名字。 那我们接下来的目的就是想看一下我们定义的这个类能不能通过 for 循环把里面的名字给取出来。 我们加入下面的代码并执行(因为这个代码下面会多次用到,我把它标注成 1 号代码):

for i in name:
    print(names)

结果:

TypeError: 'AddName' object is not iterable

他说我们定义的这个类 is not iterable, 也就是说我们定义的这个普通的类和 Iterable 没有关系,它实现不了。
那我们铁了心就是想要用 for 循环给他实现该咋办?
我们在定义的类里面加入一个额外的方法,先不管里面写什么:

def __iter__(self):
	pass

那我们现在再来执行 1号代码,看一下结果:

TypeError: iter() returned non-iterator of type 'NoneType'

结果它还是会报错,但是报的错误不一样了。那接下里我们验证一个东西,:

print("判断AddName是否是可以迭代的对象:", isinstance(AddName(), Iterable))
结果--->
判断AddName是否是可以迭代的对象: True

我们在定义的类里面不加入 def iter(self): 这个方法再来验证一下并打印结果。(不知道为啥,加粗后下滑线显示不出来了)

判断AddName是否是可以迭代的对象: False

那我们现在就非常明了了,如果想要一个对象称为可以迭代的对象,即可以使用 for ,我们就必须在这个类里面实现 def iter(self): 方法。现在已经满足了 for 循环的一个基本条件了 即 in 后面跟一个可迭代对象。
但是我们仅仅只是实现了这一个方法还不够,因为上面已经给我们报了另外一个错误:

TypeError: iter() returned non-iterator of type 'NoneType'

要解决这个问题,我们需要 def iter(self): 这个方法返回一个–>>>具有 def iter(self): 方法 和 def next(self): 方法的 对象的引用。现在它是什么东西都没有返回,为None。那我们接下来让他返回一点东西。我们在定义一个类,这个类 具有 iter方法和 next 方法。有iter方法和next方法,那他就是一个迭代器。

class NameIterable(object):	
"""这是一个迭代器, 因为他里面有 iter 和 next 方法, 二者缺一不可。"""

    def __iter__(self):
        pass

    def __next__(self):
        pass

我们再更改一下AddName类里面的 iter 方法:

def __iter__(self):
	return NameIterable()	
# NameIterable()表示创建了一个实例对象, 并让iter方法返回这个实例对象的引用。
# 得到引用后,for循环会自动调用他里面的next方法,调用一次,取出里面的一个值。

我在理一遍这个流程: 只要再定义的类里面加入 iter 这个方法, 并且它返回一个对象的引用, 这个对象必须具有 iternext方法,那么理论上讲,我们定义的这个类就可以使用 for 循环了。
那我们接下来再来捋一下当我们执行 for 循环的时候到底干了一件什么事:

for tmp in xxx:
	pass

第一步:先判断xxx是否是可以迭代。
第二步:在第一步成立的前提下,自动调用 iter 函数,得到xxx对象的__iter__方法返回值。
第三步:__iter__方法的返回值如果是一个迭代器,那么就可以实现for循环了。	
      for 循环通过这个迭代器里面的next函数来取值。取出来什么就把什么给tmp

我们再来看1号代码 ,可以看到,我们明明是把AddName对象的引用即name放到in后面去了,但是将来在最终实现的时候,iter方法返回的是谁的引用,那么for循环就会调用谁里面的next方法。
我们再来验证一下下面的代码:

from collections import  Iterator # Iterator可以判断是不是一个迭代器
print("判断AddName是否是可以迭代的对象:", isinstance(AddName(), Iterable))

print("判断NameIterable是否是迭代器:",isinstance(NameIterable(), Iterator))

-->
判断AddName是否是可以迭代的对象: True
判断NameIterable是否是迭代器:True

现在我们已经非常清楚for循环到底是怎么实现的了。下面我们就来给NameIterable的next函数增加返回值。我把完整的代码给出来

class AddName(object):

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

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

    def __iter__(self):
        return NameIterable(self)

class NameIterable(object):

    def __init__(self, obj):
        self.obj = obj
        self.index = 0  # 下标

    def __iter__(self):
        pass

    def __next__(self):
        if self.index < len(self.obj.names):
            result = self.obj.names[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration

name = AddName()
name.add("老大")
name.add("老二")
name.add("老三")

for i in name:
    print(i)

我们最终的目的是想取出老大老二老三,但是他们都存在name里面的列表中,因此如果NameIterable想要取出list里面的值,我们把name本身当作参数传给NameIterable,即:(这图可能有点丑!!!)
在这里插入图片描述
最后再来说明一下NameIterable的next函数:
如果我们不加这段代码:

        else:
            raise StopIteration

先把老大老二老三打印出来,接下来会一直打印None。加上之后它会自动判断有没有异常,只要有异常自动就停了。(可以自己试一下加上与不加上的区别,自己体验一下)

当然第二个类可以不用写,修改后是这样的(这里我不作解释了):

class AddName(object):

    def __init__(self):
        self.names = list()
        self.index = 0

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

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < len(self.names):
            result = self.names[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration

name = AddName()
name.add("老大")
name.add("老二")
name.add("老三")

for i in name:
    print(i)

补充一点: 如果一个对象是迭代器,它一定可以迭代(修改之后的代码)。反之就不一定了(第一段代码)
以上就是 迭代器迭代实现的整个过程了,说了这么多得出来的一个结论就是原来可以用for循环顶替它(哈哈哈哈哈哈哈…,确实是这么回事,但是这不是重点)。重点来了---->

3.迭代器的应用:

举个例子,如果我们想要生成100以内的数字我们可以这样写:

nums = [i for i in range(10)]

如果我们想要生成100000或者100000000000000或者更多呢?难道我们要写…算了不写了。在python2中,总之你用range(1000000000000),它会生成一个庞大的列表,占用很大的内存空间,这样做不太合适。但是在python3中这中情况就不存在了,在写range(10000000000000),它就相当于python2中的xrange(可自行百度)。
那我们看一下用到迭代器会怎么样。它的一个优点就是可以实现for循环,可以通过for循环取出里面的数据,但是它里面不是生成数据的结果,而是生成数据的方法。 打个比方:你住在很高的楼城,而且是还没有外卖的时代,想吃饭的时候不想往下跑,现在你有两种方案:1、买上一大堆食物放到冰箱里,什么时候吃什么时候取。2、自己学会做饭,什么时候吃什么时候自己做。那迭代器就好比第二种方式,什么时候用什么时候取,占用极小的内存空间。
总结: 我们可以通过迭代器,按照一定过的规律去生成一些数据,而不是将所有要迭代的数据一次性缓存下来,不用再去依赖某一个集合了,节省了很大的内存空间。

后续:

关于迭代器的知识就先说到这里,如果有时间的话,我会把生成器和装饰器的实现过程详细写出来,也算是为了以后自己如果忘记的话,可以让自己可以更快的回忆起来吧。还是那句话,有不足之处请各位帮忙斧正。
撒花–完结。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值