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模块用来创建各种迭代器。