可迭代对象 -> 可迭代对象可以迭代但不是迭代器

list, dic, str, set, tuple, f = open()(文件句柄), range(), enumerate

可迭代的不一定是迭代器,但迭代器一定是可迭代的,因为迭代器中一定有 __iter__ 方法

1. 可迭代: Iterable(可循环)

可迭代协议:只要含有__iter__方法都是可迭代
  • dir() -> 查看对象所拥有的方法 -> 判断是否为可迭代的 -> 常用方法

iterable_fn = dir([])

print('__iter__' in iterable_fn) # True

  • isinstance() -> 判断一个对象是不是某个东西 -> 判断是否为可迭代的

from collections import Iterable
from collections import Iterator

print(isinstance([], Iterable)) # True
print(isinstance([], Iterator)) # False

2. 迭代器: Iterator -> 迭代器可以被 for 循环

迭代器的好处: 

  • 从容器类型中一个一个的取值,会把所有的值都取到,而且所有数据只能取一次,取完就没有。(一定要记住该原则
  • 节省内存空间 -> 迭代器并不会在内存中再占用一大块内存,而是随着循环每次生成一个,每次执行__next__()就会每次返回一个

迭代器协议: 内部含有 __next__ 和 __iter__方法的就是迭代器 -> __iter__方法 位于 Iterable里面,通过调用 __iter__ 方法返回一个迭代器 -> Iterator: 迭代器 

  • 判断是否是迭代器 -> 判断是否有 __next__ 和 __iter__ 方法

def is_iterator(obj):
    list_fn = dir(obj)
    iter_result = '__iter__' in list_fn
    if iter_result:
        iter_fn = dir(obj.__iter__())
        iterator_result = '__next__' in iter_fn
        if iterator_result:
            return '是迭代器'
        else:
            return '可迭代,但不是迭代器'
    else:
        return '不可迭代'


print(is_iterator([]))

  • 获取迭代器: .__iter__()  -> 当可迭代对象调用了 .__iter__() 会返回一个迭代器

iterator = [].__iter__() # <list_iterator object at 0x000001EE4B5C3550> 看到 iterator 单词就应该知道它是一个迭代器

  • 查看只属于迭代器的方法 -> dir([1,2].__iter__())是列表迭代器中实现的所有方法,dir([1,2])是列表中实现的所有方法,都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合,然后取差集。

# print(dir([1,2].__iter__()))
# print(dir([1,2]))

print(set(dir([1, 2].__iter__())) - set(dir([1, 2]))) # {'__next__', '__length_hint__', '__setstate__'}

  • __length_hint__() -> 获取迭代器中元素的长度

iterator = [1, 2, 3, 4, 5, 6].__iter__()

print(iterator.__length_hint__()) # 6

  • __next__() -> 一个一个的取值 -> 可以不依赖 for 循环挨个取值 ->  如果我们一直取next取到迭代器里已经没有元素了,就会抛出一个异常StopIteration,告诉我们,列表中已经没有有效的元素了。

iterator = [1, 2, 3, 4, 5, 6].__iter__()

print(iterator.__next__()) # 1
print(iterator.__next__()) # 2
print(iterator.__next__()) # 3

  • __setstate__ -> 根据索引值指定从哪里开始迭代

iterator = [1, 2, 3, 4, 5, 6].__iter__()

iterator.__setstate__(3)
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())

iterator = [1, 2, 3, 4, 5, 6].__iter__()
iterator.__setstate__(3)

for i in iterator:
    print(i)

3. for 循环的执行机制 -> 了解就可以

在执行 for 循环的时候它首先会判断该对象是否是可迭代的,然后再判断是否是迭代器,如果是迭代器再执行迭代器中 __next__方法

4. for 循环 和 迭代器的区别 

  • for循环: 多个 for 循环 循环一个可迭代对象的时候,for循环会在自身内部生成一个迭代器

lis = [1, 2, 3, 4]
for i in lis:
    print(i)

for i2 in lis:
    print(i2)

  • 迭代器: 如果迭代器里的值都被取过一遍以后(取完了), 那么迭代器再被调用的时候里面的值是空的 -> 因为迭代器里的值只能取一遍,取完就没有了

lis = [1, 2, 3, 4]
iterator = lis.__iter__()

i_lis = list(iterator)  # [1, 2, 3, 4] 此时,迭代器里的所有值都给了 list
i_lis2 = list(iterator) # [] 迭代器里的值都被取完了

5. iter() 和 next() 

  • iter() 等同于 __iter__() next() 等同于 __next__() 在以后日常使用中尽量使用 iter() 和 next(),在这里使用 __iter__() 和 __next__() 是为了说明

  • iter(可迭代对象)

lis = [1, 2, 3]
iterator = iter(lis)

# 等同于

iterator = lis.__iter__()

  • next(迭代器)

lis = [1, 2, 3]
iterator = iter(lis)

num = next(iterator)

# 等同于

num = iterator.__next__()

5. 总结

  • 迭代是访问集合元素的一种方式,迭代器是一个能够记住遍历位置的对象
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问完结束
  • 如果想访问一个元素,需要把这个元素前面的所有元素都遍历后,才可以访问

  • 凡是可作用于 for 循环的对象都是 Iterable(可迭代)
  • 凡是可作用于 __next__() 的函数的对象都是Iterator (迭代器)
  • __iter__()函数用于把Iterable(可迭代)容器,变成Iterator(迭代器)