Python iterable(可迭代) 和 iterator(迭代器) 底层源码剖析,简称【迭代器圣经】-CSDN 博客

迭代器和和迭代类型

文章目录

1. 概念明确

1.1 iterable 可迭代的

# 源码在_collections_abc.py
class Iterable(metaclass=ABCMeta):
    @abstractmethod
    def __iter__(self): ...

1.2 iterator 迭代器

# 源码在_collections_abc.py
class Iterator(Iterable):
    @abstractmethod
    def __next__(self): 
        raise StopIteration
    def __iter__(self): 
        return self

2. 如何查看对象是iterable还是iterator

2.1 通过代码判断

from collections.abc import Iterable, Iterator

my_obj = [1, 2, 3]
print(isinstance(my_obj, Iterable))  # True
print(isinstance(my_obj, Iterator))  # False

2.2 通过是否实现相应方法

3. iter()方法的两个作用

from collections.abc import Iterable, Iterator

my_obj = iter([1, 2, 3])
print(isinstance(my_obj, Iterable))  # True
print(isinstance(my_obj, Iterator))  # True

# 抛骰子,当抛到5的时候程序停止
import random
from collections.abc import Iterable, Iterator

def fun():
    return random.randint(1, 6)

my_obj = iter(fun, 5)
for i in my_obj:
    print(i)
# 这里是随机的结果哦
# 3
# 4
# 4
# 6
# 1
# 1

print(isinstance(my_obj, Iterable))  # True
print(isinstance(my_obj, Iterator))  # True

4. for ... in ... 到底做了什么?

4.1 for in 必须是 iterator 类型

# for ... in ... 到底干了什么事?
import dis

def func():
    for i in 'abcd':
        pass
dis.dis(func)
"""输出结果如下
  5           0 LOAD_CONST               1 ('abcd')
              2 GET_ITER
        >>    4 FOR_ITER                 4 (to 10)
              6 STORE_FAST               0 (i)

  6           8 JUMP_ABSOLUTE            4
        >>   10 LOAD_CONST               0 (None)
             12 RETURN_VALUE
"""
# 首先LOAD_CONST加载了一个字符串变量
# 然后执行GET_ITER,也就是说会对for in后面的对象自动执行 `iter("abcd")`操作,目的就是将可迭代的数据类型转成迭代器,参见官方文档:
# https://docs.python.org/zh-cn/3/library/dis.html#opcode-GET_ITER

4.2 为什么 for in 必须是 iterator 类型?

# iterable可迭代数据类型无法调用next()方法,因为没有实现__next__方法
# 换句话说list类型不是迭代器所以没有实现__next__方法,也就无法使用next()函数进行调用
lt = [1,2,3]
next(lt) # TypeError: 'list' object is not an iterator

# 自定义Person类,并实现了__next__方法,从而实现使用next()函数进行调用
# 只实现__next__方法时,即不是Iterable也不是Iterator
class Person:
    def __init__(self):
        self.result = 0

    def __next__(self):
        self.result += 1
        if self.result >= 3:
            raise StopIteration("停止遍历")
        return self.result

p1 = Person()
print(next(p1))  # 1
print(next(p1))  # 2
# print(next(p))  # 抛出StopIteration异常: 停止遍历


p2 = Person()
from collections.abc import Iterable, Iterator
print(isinstance(p2, Iterable))  # False
print(isinstance(p2, Iterator))  # False


for i in p2:  # 抛出TypeError异常: 'Person' object is not iterable
    print(i)  # 1 2

4.3 为什么 for in 不会抛出StopIteration异常?

5. 哪些系统类型是可以 for in 遍历的?

6. 自定义类型如何实现 for in?

三种方式:

实现__iter__方法

# 方式一:只实现__iter__方法,所以此时Person对象是可迭代对象
class Person:
    def __init__(self):
        self.result = 0
    def __iter__(self): # 注意__iter__方法必须返回迭代器对象
        return iter([1,2,3,4,5])
p = Person()
# print(next(p)) # TypeError: 'Person' object is not an iterator
for i in p:
    print(i)
from collections.abc import Iterable, Iterator
print(isinstance(p, Iterator))  # False
print(isinstance(p, Iterable))  # True
# 1
# 2
# 3
# 4
# 5

实现__iter__方法和__next__方法

# 方式二:必须同时实现__iter__和__next__才是迭代器对象
class Person:
    def __init__(self):
        self.result = 0

    def __iter__(self):
        print("iter")
        self.result = 0 # 回退结束条件
        return self

    def __next__(self):
        self.result += 1
        if self.result >= 6:
            raise StopIteration("停止遍历")
        return self.result

p1 = Person()
for i in p1:
    print(i)
from collections.abc import Iterator, Iterable
print(isinstance(p1, Iterator)) # True
print(isinstance(p1, Iterable)) # True

p2 = Person()
print(next(p2)) # 1
print(next(p2)) # 2
print(next(p2)) # 3

实现__getitem__方法

# 方式三:只实现__getitem__方法,此时即非迭代器也非可迭代对象
class Person:
    def __init__(self):
        self.result = 0
    def __getitem__(self, item):
        self.result +=1
        if self.result >=6:
            raise StopIteration("停止遍历")
        return self.result
p = Person()
# print(next(p)) # TypeError: 'Person' object is not an iterator
for i in p:
    print(i)
# 1
# 2
# 3
# 4
# 5
from collections.abc import Iterable, Iterator
print(isinstance(p, Iterator))  # False # 判断是否是迭代器对象
print(isinstance(p, Iterable))  # False # 判断是否是可迭代对象

# 证明__iter__的优先级比__getitem__要高,只会打印“iter”并报错
class Person:
    def __init__(self):
        self.result = 0
    def __getitem__(self, item):
        print("getitem") 
    def __iter__(self):
        print("iter") # iter
p = Person()
for i in p: # TypeError: iter() returned non-iterator of type 'NoneType'
    print(i)

7. 为什么 Python 要给出 iterable、iterator 两种数据类型?为什么不直接只定义 iterator 呢?

from collections.abc import Iterable, Iterator

f = open("data.txt")
print(isinstance(f, Iterator))  # True,这说明f是个迭代器
print(isinstance(f, Iterable))  # True
# 我们知道这个f是可以放到for in中进行遍历的
for i in f:
    pass
print(f.read())  # 我们发现这里打印是空的,这就说明当我们对f这个迭代器进行遍历完成后,再使用read()获取里面内容是获取不到的


lt = [1, 2, 3]  # list是可迭代对象
for i in lt:
    pass
print(lt)  # 而这里会打印[1, 2, 3],遍历完list依旧在!

my_lt = iter([1, 2, 3]) # 将可迭代类型的数据转成迭代器
for i in my_lt:
    pass
print(list(my_lt))  # [],这里打印的是空的,说明迭代器遍历完后,里面的内容就没有了


f.close()

8. 正视__getitem__

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    # 使用__getitem__魔法函数加强class类型的Company,使其视为iterable可迭代类型及subscriptable
    def __getitem__(self, item):
        return self.employee[item]


company = Company(["tom", "bob", "jane"])
for em in company:
    print(em)
    
print(company[1])

company1 = company[:2]
for em in company1:
    print(em)

print(len(company1))

9. 结论

不用点赞,因为你看过就会了,别人没看过就永远不知所以然!

全文完
本文由 简悦 SimpRead 转码,用以提升阅读体验,原文地址