【Python测试开发】迭代对象与迭代器的原理及应用

1 Iterable对象

我们知道在python中,可以对list、tuple、str、dict等类型的数据,使用for…in…语法从其中拿到数据进行使用。我们把这样的过程称为遍历,也叫迭代,这样的可以被for循环作用的数据类型叫做Iterable类型。

但是如果我们对int类型数据进行for…in…迭代的话,会得到错误提示TypeError: 'int' object is not iterable,提示int并不是iterable对象。

可以使用isinstance()判断一个对象是否是Iterable对象。

from collections import Iterable

print(isinstance([], Iterable))
print(isinstance((), Iterable))
print(isinstance({}, Iterable))
print(isinstance(set(), Iterable))
print(isinstance("abc", Iterable))
print(isinstance(123, Iterable))

由此可看到,list、tuple、dict、set、str都是Iterable类型,可以被作用于for循环,但是int不是Iterable类型,不可以被作用于for循环。

我们将list、tuple、dict、set、str传到dir()函数,发现输出中都包含__iter__方法。而int传到dir()函数中,却没有__iter__方法。这就是Iterable类型与非Iterable类型的本质区别。

Iterable类型的对象都有一个__iter__方法,而非Iterable类型的对象没有__iter__方法。换句话说,一个具备了__iter__方法的对象,就是一个Iterable类型的对象。

除了前面介绍的list、tuple、dict、set、str,文件对象和socket对象也是Iterable类型的,这些对象都可以被for…in…迭代即可。

2 迭代器Iterator

为什么Iterable对象可以作用于for…in…迭代呢?是因为迭代器Iterator。

我们在用for…in…迭代一个Iterable对象的时候,先通过iter()函数获取该对象提供的一个Iterator,然后通过next()方法来依次获取对象中的每一个数据,这就是for循环的本质。
可以通过下面的方法,演示一下for循环的实现过程。

import sys

li = [1, 2, 3] # Iterable对象
it = iter(li)  # 创建迭代器,实际会调用li.__iter__()
while True:
    try:
        print(next(it)) # next会调用it.__next__()
    except StopIteration:
        sys.exit()

通过iter()函数获取可迭代对象li的迭代器,从第一个元素开始,使用next()不断获取下一个元素,最后到达最后一个元素,将数据一个不漏滴都走一遍。过程可以用下图来表示:
在这里插入图片描述
当然在实际使用时,我们使用for来实现迭代,for…in…将上面的过程隐式了。

li = [1, 2, 3, 4]

for l in li:
    print(l)

迭代器Iterator就像一个数据工厂,每次你对它使用next()函数,它都给你返回一个值或者是StopIteration异常。

3 迭代工具

很少有需求要我们自己定义一个迭代器,更多时候,我们只要会处理和使用Iterable对象就可以了。除了for能接收Iterable对象,any() 、all()也可以对迭代参数进行处理。

any() 函数用于判断给定的可迭代参数是否全部为 False,是则返回 False,如果有一个为 True,则返回 True。元素除了是 0、空、False 外都算 True。

any([0, '', False]) # 返回False
any((0, '', False,'a')) #返回True

all() 函数用于判断给定的可迭代参数中的所有元素是否都为 True,如果是返回 True,否则返回 False。元素除了是 0、空、None、False 外都算 True。

all(['a', 'b', 'c', 'd']) #返回True
all(['a', 'b', '', 'd']) #返回False

4 有哪些典型的迭代器

在Python中有很多迭代器iterator的例子,itertools模块里面的函数返回的都是迭代器。

下面举几个实际的例子。

  • 例子一:cycle()

cycle()创建一个迭代器,把传入的一个序列无限迭代下去

from itertools import islice
colors = cycle(['red', 'white', 'blue'])  # 无限循环输出
limited = islice(colors, 0, 4)            # 有限循环
for x in limited:                         # so safe to use for-loop on
    print(x)
red
white
blue
red
  • 例子二:count()

count()创建一个迭代器,从第一参数开始,以第二个参数作为步长,无限迭代下去

import itertools
for c in itertools.count(12, 1):
    print(c)
  • 例子三:repeat()

repeat()负责把第一个参数无限重复下去,重复次数是第二个参数来指定

import itertools
for c in itertools.repeat('A', 5):
    print(c)

前面是三个是无限iterator,下面介绍几个常用的有限迭代器。

  • 例子四:takewhile()

takewhile()创建一个迭代器,按顺序对序列进行判断,迭代输出满足条件的元素,当条件不满足时终止。下面的代码将输出[1, 4, 6]。

import itertools

print(list(itertools.takewhile(lambda x: x < 7, [1, 4, 6, 8, 4, 1]))) 

我们可以将迭代器传递给list()或者tuple(),将迭代器转换成列表或者元祖,以便打印输出。

  • 例子五:accumulate()

accumulate()创建一个迭代器,返回累加之和或者两个函数的累计结果,accumulate的默认操作是相加,第二个参数可以指定累计的函数,比如operator.mul代表相乘。

import itertools
import operator

print(list(itertools.accumulate([1, 2, 3, 4, 5]))) # 输出[1, 3, 6, 10, 15]
print(list(itertools.accumulate([1, 2, 3, 4, 5], operator.mul))) # 输出[1, 2, 6, 24, 120]
  • 例子六:chain()

chain()把一组迭代对象串联起来,形成一个更大的迭代器

import itertools

print(list(itertools.chain('ABC', 'DEF'))) # 输出['A', 'B', 'C', 'D', 'E', 'F']
  • 例子七:combinations()

combinations()创建一个迭代器,用于返回输入的迭代对象元素的组合

import itertools

print(list(itertools.combinations("ABC", 2))) # 输出[('A', 'B'), ('A', 'C'), ('B', 'C')]
  • 例子八:compress()
    compress()创建一个迭代器,通过第二个迭代对象对第一个迭代对象进行过滤。
import itertools

print(list(itertools.compress("ABC", [1, 0, 1]))) # 输出['A', 'C']
  • 例子九:dropwhile()

与takewhile相反,将每个元素传递给lambda函数,如果lambda函数返回True,那么这个元素就会被丢弃,一旦我们到达元素8,lambda函数返回False,我们就获得8及它之后的元素。

import itertools

print(list(itertools.dropwhile(lambda x: x < 7, [1, 4, 6, 8, 4, 1]))) # 输出[8, 4, 1]
  • 例子十:groupby()

groupby迭代器将会从迭代对象中返回连续的keys和groups。通常需要提前将迭代对象进行排序。

from itertools import groupby

vehicles = [('Ford', 'Taurus'), ('Dodge', 'Durango'), ('Chevrolet', 'Cobalt'), 
            ('Ford', 'F150'), ('Dodge', 'Charger'),('Ford', 'GT')]

sorted_vehicles = sorted(vehicles, key=lambda m: m[0]) # 以元组中的第0个元素为key,对列表进行排序。

for key, group in groupby(sorted_vehicles, lambda m: m[0]):
    print(key)
    for make, model in group:
        print("{model} is made by {make}".format(model=model, make=make))
    print("***** End of Group *****\n")
  • 例子十一:filterfalse()

filterfalse返回那些评估为False的值。与compress有点类似。

import itertools

print(list(itertools.filterfalse(lambda x: x > 5, [6, 7, 8, 9, 1, 2, 3, 10])))

-例子十二: islice()
与序列的切片操作类似。不过它可以切迭代器比如文件对象。

import itertools

with open('a.txt', 'r') as f:
    for i in itertools.islice(f, 0, 3):  # 生成包含0到3行的生成器,不包含第3行
        print(i)
    for i in itertools.islice(f, 100):  # 生成100行以内的生成器
        print(i)

更多实用工具可以参考官方文档

5 自定义一个迭代器

为了更深入的理解迭代器,我们可以自己创建一个迭代器。

前面我们通过iter获得了一个迭代器,通过dir(it)可以看到Iterator具有两个特殊的成员方法__iter__()和__next__()。其中__iter__()方法返回迭代器对象本身,next()方法返回容器的下一个元素,直到结尾抛出StopIteration异常。

如果我们在类中实现__iter__()和__next__()方法,这个类的对象就是一个迭代器了。这样的类对象就可以被for…in…来进行迭代了。

下面我写一个与list_iterator相同行为的迭代器:

class ListIter(object):
    def __init__(self, data):
        self.__data = data
        self.__count = 0

    def __iter__(self):  # 套路一:必须实现__iter__,返回对象本身
        return self

    def __next__(self):  # 套路二:必须实现__next__,返回下一个元素,直到结尾抛出StopIteration异常。
        if self.__count < len(self.__data):
            val = self.__data[self.__count]
            self.__count += 1
            return val
        else:
            raise StopIteration

我们就可以使用for循环来遍历这个迭代器了:

a = ListIter([1, 2, 3, 4, 5])
for i in a:
    print(i)

6 总结

可以直接作用于for循环的对象统称为Iterable对象。

Python的for循环迭代机制是借助迭代器Iterator实现的。

用for迭代一个Iterable对象的时候,先通过iter()函数获取该对象提供的一个Iterator,然后通过next()方法来依次获取对象中的每一个数据。这就是for的内部实现原理。

除了for之外,all()和any()也可以对迭代对象进行处理。Python提供了itertools模块用来创建各种迭代器。

参考文章

  1. https://www.cnblogs.com/zhbzz2007/p/6028688.html
  2. https://cloud.tencent.com/developer/article/1441182
  3. https://nvie.com/posts/iterators-vs-generators/
  4. https://nvie.com/posts/use-more-iterators/
发布了187 篇原创文章 · 获赞 270 · 访问量 172万+

猜你喜欢

转载自blog.csdn.net/liuchunming033/article/details/97271968
今日推荐