iterable
可迭代的# 源码在_collections_abc.py class Iterable(metaclass=ABCMeta): @abstractmethod def __iter__(self): ...
__iter__
方法iterator
迭代器# 源码在_collections_abc.py class Iterator(Iterable): @abstractmethod def __next__(self): raise StopIteration def __iter__(self): return self
__iter__
方法和__next__
方法iterable
还是iterator
?from collections.abc import Iterable, Iterator my_obj = [1, 2, 3] print(isinstance(my_obj, Iterable)) # True print(isinstance(my_obj, Iterator)) # False
__iter__
方法__iter__
方法和__next__
方法iter()
方法的两个作用iter(iterable) -> iterator
:将可迭代对象转成迭代器 from collections.abc import Iterable, Iterator my_obj = iter([1, 2, 3]) print(isinstance(my_obj, Iterable)) # True print(isinstance(my_obj, Iterator)) # True
iter(callable, sentinel) -> iterator
:将可执行的对象,按照停止的条件,生成迭代器对象# 抛骰子,当抛到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
for ... in ...
到底做了什么?for ... in ...
其实是for ... in <iterator>
in
后面的对象 必须是 iterator 迭代器iter(iterable)
操作),这也就是解释了为什么我们遍历可迭代对象也是可以的# 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
for ... in ...
其实就是每次循环都自动调用next()
方法# iterable可迭代数据类型无法调用next()方法,因为没有实现__next__方法 # 换句话说list类型不是迭代器所以没有实现__next__方法,也就无法使用next()函数进行调用 lt = [1,2,3] next(lt) # TypeError: 'list' object is not an iterator
__next__
方法就可以for in
了呢?不可以!# 自定义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
StopIteration
异常?__next__
方法抛出StopIteration
异常时,就停止循环了。next()
方法不会帮我们处理StopIteration
异常int
类型、bool
类型:不可以,因为它们即没有实现__getitem__
,也没有实现__iter__
str
类型、list
类型、set
类型、dict
类型、tuple
类型:可以,因为它们实现了__iter__
三种方式:
__iter__
方法,从而变成 Iterable 类型__iter__
方法和__next__
方法,从而变成 iterator 类型__getitem__
方法,只要实现了__getitem__
方法,就可以被 **【视为可迭代的】**,注意是视为,并不是真正意义上的 Iterable 可迭代对象!!
__getitem__
方法的对象被 **【视为可迭代对象】,并不是真正的 Iterable 类型 **。Python 的内置函数iter()
会尝试调用__iter__
方法获取迭代器,如果没有实现__iter__
方法,那么 Python 会尝试调用__getitem__
方法,从索引 0 开始获取元素,直到IndexError
为止。因此,只要对象实现了__getitem__
方法,就可以被视为可迭代对象。__getitem__
执行的优先级比__iter__
要低。__iter__
方法__next__
方法,所以不能使用next()
,会报错# 方式一:只实现__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__
方法__next__
方法的,所以可以使用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)
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()
__getitem__
__getitem__
方法的作用是使类的实例对象表现得像一个序列(如列表、元组等)或映射(如字典)一样可索引。__getitem__
方法时,该类的实例对象可以使用索引运算符([]
)来访问元素。__getitem__
方法的对象是Sequence
类型。
collections.abc
模块定义了__getitem__
方法的抽象基类,名为Sequence
。如果一个类实现了__getitem__
方法,那么它就可以被认为是Sequence
类型,也就是可下标访问的,即 "subscriptable"。__getitem__
方法的对象。在类型检查时,你可以使用collections.abc.Sequence
来检查一个对象是否实现了__getitem__
方法,即是否是 "subscriptable”。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))
Iterable
可迭代对象,则一定可以使用for ... in ...
进行遍历for ... in ...
进行遍历,则这个对象不一定是可迭代对象,因为实现__getitem__
方法也可以通过for ... in ...
进行遍历。iterator
对象是一定可以使用next()
函数进行访问的。next()
函数访问的不一定是Iterable
对象。因为能进行next()
函数访问只有一个要求就是实现__next__
方法。__iter__
和__next__
。iter()
函数转成对应的迭代器对象,则此对象要么实现__getitem__
方法,要么实现 __iter__
方法,要么是个可调用方法。不用点赞,因为你看过就会了,别人没看过就永远不知所以然!