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__方法,要么是个可调用方法。不用点赞,因为你看过就会了,别人没看过就永远不知所以然!