1.迭代器
我们知道,对于一个列表而言,想要得到列表中的元素,有2种方法可以实现:
1.索引 (切片的本质也是索引)
2.循环*
在python中可以进行循环的数据类型有:
列表list,元组tuple,集合set,字符串str,字典dict, range();
文件句柄 f = open() for line in f
枚举 enumerate
这是因为以上数据类型都是可迭代的。
介绍一个函数:
dir()
参数是一个数据类型,例如:[], {}…
返回值:此种数据类型的所有方法,返回值类型:列表
双下方法的概念:
一般称某些双下划线的方法为:双下方法
这种方法一般是已经写好的C语言的代码,可以通过不止一种方式进行调用
一般情况下,双下方法不直接被我们调用,通过其他语法触发的
例如:
print([1].__add__([2]))
# 下面与上面等价,下面实际上就是调用上面的方法
print([1]+[2])
打印列表的所有方法:
print(dir([]))
我们需要注意到列表的方法中含有__iter__方法
判断其他可以使用for循环的数据类型都含有__iter__方法,不能使用for循环的数据类型不含有__iter__方法:
# 不可使用for循环
print('__iter__' in dir(int))
print('__iter__' in dir(bool))
# 可以使用for循环
print('__iter__' in dir(list))
print('__iter__' in dir(dict))
print('__iter__' in dir(set))
print('__iter__' in dir(tuple))
print('__iter__' in dir(enumerate))
结果如下:
因此,我们称含有__iter__方法的数据类型都是可迭代的 --> 可迭代协议
对于一个可迭代的数据类型,调用__iter__方法后便会生成一个迭代器(iterator),例如:
print([].__iter__())
可以看到,iter()方法返回一个迭代器对象:
迭代器也是一种数据类型,我们同样可以使用dir()函数来查看迭代器类型的方法,与可迭代的数据类型对比,我们可以看到迭代器所特有的方法为:
# 查看迭代器的方法
print(dir([].__iter__()))
# 查看迭代器所特有的方法
print(set(dir([].__iter__()))-set(dir([])))
迭代器的特有方法如下:
其中,__setstate__指定指针位置,__length_hint__求元素的个数
我们需要关注的重点是__next__方法,此方法用于逐个取迭代器中的值
li = [1,2,3]
# 定义一个迭代器
iterator = li.__iter__()
# 取迭代器中的值
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
输出结果如下:
当取值的数目超过迭代器中值的数目时就会报错:
li = [1,2,3]
# 定义一个迭代器
iterator = li.__iter__()
# 依次取4个值
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
运行时报错如下:
因此,内部含有__next__和__iter__方法的都是迭代器(必须同时含有) --> 迭代器协议
则当我们进行一个for循环时,其原理如下:
li = []
iterator = li.__iter__() # 首先拿到一个迭代器
i = iterator.__next__() # 使用next方法取值
# 直到报错的时候循环结束
2.判断是否可迭代&迭代器
注意:使用python3.3以下版本,导入库时以 from collections导入,3.3以上版本应使用 from collections.abc形式导入,之前的导入方式在3.3以上的pyton解释器中已经被废弃,且在3.9版本中将不能再使用。
from collections.abc import Iterable
from collections.abc import Iterator
# 判断列表是否是一个迭代器
print(isinstance([],Iterator))
# 判断列表是否是可迭代的
print(isinstance([],Iterable))
输出结果如下:
我们可以看到,列表虽然是可迭代的数据类型,但它并不是一个迭代器,
因此,可迭代的并不一定是迭代器。
我们做一个小测试:
from collections.abc import Iterable
from collections.abc import Iterator
class A:
def __iter__(self):pass
def __next__(self):pass
a = A()
print(isinstance(a,Iterator))
print(isinstance(a,Iterable))
若A类即含有__iter__方法和__next__方法,则它是一个迭代器并且可迭代:
from collections.abc import Iterable
from collections.abc import Iterator
class A:
def __iter__(self):pass
# def __next__(self):pass
a = A()
print(isinstance(a,Iterator))
print(isinstance(a,Iterable))
当A类中只含有__iter__方法,则它可迭代但不是一个迭代器:
from collections.abc import Iterable
from collections.abc import Iterator
class A:
# def __iter__(self):pass
def __next__(self):pass
a = A()
print(isinstance(a,Iterator))
print(isinstance(a,Iterable))
当A类中只含有__next__方法,则它既不可迭代也不是一个迭代器:
因此:
只要含有__iter__和__next__方法的数据类型都是迭代器
只含有__iter__方法的仅仅可迭代
只含有__next__方法的既不可迭代又不是迭代器
总结
1.内部含有__iter__方法的数据类型都是可迭代的,都可以使用for循环 – 可迭代协议。
2.内部同时含有__iter__方法和__next__方法的都是迭代器 – 迭代器协议、
3.可以被for循环的都是可迭代的
4.可迭代的内部都含有__iter__方法
5.迭代器一定可迭代,但是可迭代的不一定是迭代器(__next__方法不一定存在)
6.可迭代的.__iter__方法就能得到一个迭代器
7.迭代器中的__next__方法可以一个一个的获取值
8.优势:
从容器类型中一个一个的取值,会把所有的值都取到;
可以节省内存空间:
迭代器并不会在内存中再占用一大块内存,
而是随着循环,每次生成一个,然后每次next给我一个
9.应用: for循环
可迭代对象的特点:
并不是一次性就生成好的,而是在需要时一个一个给你
例如:range(1000) 并不是直接生成1000个数在内存中
但是对于像列表这种有值存在的,其值是存在于内存中的
因此,使用迭代器可以节省内存