p14_可迭代对象 & 迭代器 & 生成器

可迭代对象 & 迭代器 & 生成器

判断 Iterable 、 Iterator 、 Generator

从 collections 模块(或 typing 模块)中导入这三个类型,通过

isinstance 来判断对象类型

from collections.abc import Iterable, Iterator, Generator
num1 = 123
str1 = "abc"
list1 = [ 1 , 2 , 3 ]
tup1 = ( 1 , 2 , 3 )
dict1 = {"one": 1 , "two": 2 , "three": 3 }
set1 = { 1 , 2 , 3 }
"""Python标准数据类型中, 除了number, 其他都是可迭代对象, 即可
以用for循环来遍历"""
print(isinstance(num1, Iterable))  # False
print(isinstance(str1, Iterable))  # True
print(isinstance(list1, Iterable))  # True
print(isinstance(tup1, Iterable))  # True
print(isinstance(dict1, Iterable))  # True
print(isinstance(set1, Iterable))  # True
print(isinstance(num1, Iterator))  # False
print(isinstance(str1, Iterator))  # False
print(isinstance(list1, Iterator))  # False
print(isinstance(tup1, Iterator))  # False
print(isinstance(dict1, Iterator))  # False

可迭代对象

只要满足以下条件之一即可:

支持迭代协议(有__iter__()方法)

print(isinstance(set1, Iterator))  # False
print(isinstance(num1, Generator))  # False
print(isinstance(str1, Generator))  # False
print(isinstance(list1, Generator))  # False
print(isinstance(tup1, Generator))  # False
print(isinstance(dict1, Generator))  # False
print(isinstance(set1, Generator))  # False
""" range对象是可迭代对象 """
range1 = range( 4 )
print(isinstance(range1, Iterable))
""" reversed对象是迭代器 """
reversed1 = reversed([ 1 , 3 , 1 , 4 ])
print(isinstance(reversed1, Iterator))
""" zip对象是迭代器 """
zip1 = zip([ 1 , 2 , 3 ], ["one", "two", "three"])
print(isinstance(zip1, Iterator))
""" enumerate对象是迭代器 """
enumerate1 = enumerate([ 1 , 2 , 3 ])
print(isinstance(enumerate1, Iterator))

支持序列协议(有__getitem__()方法,且数字参数从 0 开始)

自定义可迭代对象

只要支持迭代协议或者序列协议即可

num1 = 123
str1 = "abc"
list1 = [ 1 , 2 , 3 ]
tup1 = ( 1 , 2 , 3 )
dict1 = {"one": 1 , "two": 2 , "three": 3 }
set1 = { 1 , 2 , 3 }
range1 = range( 4 )
""" str, list, tuple, dict, set, range对象都支持迭代协议,
所以他们都是可迭代对象
而 number 对象既不支持迭代协议,也不支持序列协议,因此它不是可迭
代对象 """
print('__iter__' in dir(num1) or '__getitem__' in
dir(num1))  # False
print('__iter__' in dir(str1))
print('__iter__' in dir(list1))
print('__iter__' in dir(tup1))
print('__iter__' in dir(dict1))
print('__iter__' in dir(set1))
print('__iter__' in dir(range1))
from typing import Iterable
class MyObject1:

def init(self):
pass

mo1 = MyObject1()
print(isinstance(mo1, Iterable))  # False: 既不支持迭代协
议, 也不支持序列协议

class MyObject2:
def init(self):
pass

def iter(self):
pass

mo2 = MyObject2()
print(isinstance(mo2, Iterable))  # True: 因为有__iter__()
方法, 即支持迭代协议

class MyObject3:
def init(self):
pass

def getitem(self, index):
pass

mo3 = MyObject3()
print(isinstance(mo3, Iterable))  # False

迭代器

支持迭代器协议(注意区分迭代协议),即同时满足下面两个条件:

实现__iter__()方法

实现__next__()方法

“”"
mo3支持序列协议, 为何 isinstance 判断为 False 呢?
注意: 使用 isinstance(obj, Iterable) 可以检测obj是否有
__iter__()方法,
但是无法检测是否能够使用__getitem__()方法进行迭代
"""
reversed1 = reversed([ 1 , 3 , 1 , 4 ])
zip1 = zip([ 1 , 2 , 3 ], ["one", "two", "three"])
enumerate1 = enumerate([ 1 , 2 , 3 ])
print('__iter__' in dir(reversed1) and '__next__' in
dir(reversed1))
print('__iter__' in dir(zip1) and '__next__' in
dir(zip1))
print('__iter__' in dir(enumerate1) and '__next__' in
dir(enumerate1))

自己创建迭代器

只要支持迭代器协议即可

from typing import Iterator
class MyObject1:
def __init__(self):
pass
mo1 = MyObject1()
print(isinstance(mo1, Iterator))  # False: 不支持迭代器协议
class MyObject2:
def __init__(self):
pass
def __iter__(self):
pass
mo2 = MyObject2()
print(isinstance(mo2, Iterator))  # False: 不支持迭代器协议
class MyObject3:
def __init__(self):
pass
def __iter__(self):
pass

迭代的逻辑

√ 迭代的时候,先找 iter() 方法,如果没有的话,再找 getitem()

方法

√ 如果有 iter() 方法:

① 可迭代对象里的__iter__()方法返回一个迭代器,通过迭代器里的

next()方法实现迭代

② 迭代器里的__iter__()方法返回它本身(因为迭代器协议中包含了迭

代协议,所以迭代器也一定是可迭代对象,可迭代对象的__iter__()方

法要返回一个迭代器,所以只需要返回本身即可)

③ 迭代器里的__next__()方法返回可迭代对象的下一项,如果没有下一

项可返回,则抛出 StopIteration 异常

def __next__(self):
pass
mo3 = MyObject3()
print(isinstance(mo3, Iterator))  # True: 支持迭代器协议
class ContainerIterator:
def __init__(self, container):
self.container = container
self.cursor = 0
def __iter__(self):  # 对应第 ② 点
return self
def __next__(self):  # 对应第 ③ 点
if self.cursor < len(self.container.iterable):

item = self.container.iterable[self.cursor]
self.cursor += 1
return item
raise StopIteration

class Container:
def init(self, iterable):
self.iterable = iterable

def iter(self):  # 对应第 ① 点
return ContainerIterator(self)

cont = Container([ 4 , 5 , 6 ])

“”"
for语句在执行时会先调用可迭代对象的__iter__()方法, 得到该方法返
回的迭代器对象,
然后每循环一次, 该迭代器对象调用一次__next__()方法, 通过
next()方法来逐一返回元素,
当元素用尽时, next()方法将抛出StopIteration异常, 而for语
句会捕获这个异常来break循环
“”"
for item in cont:
print(item)

“”" for循环原理实现 “”"
cont_iterator = cont.iter()
while True:
try:
item = cont_iterator.next()
print(item)
except StopIteration:

break

“”"

除了for循环以外, 其他能够接收iterable参数的函数或方法大多都是基
于类似原理,
比如: list()、tuple()、set()、sum()等等 “”"
print(list(cont))
print(tuple(cont))
print(set(cont))
print(sum(cont))

“”"

迭代器也是可迭代对象, for语句在执行时会先调用迭代器的__iter__()
方法,
而迭代器的__iter__()方法正好返回迭代器对象本身, 后面过程就是一
样的了"“”
cont_iterator = cont.iter()
for k in cont_iterator:
print(k)

“”"

同一个迭代器只能往前不会后退, 这里print(j)不会出结果, 是因为for
语句遍历的
迭代器对象是cont_iterator, 在上文中该迭代器的self.cursor属性
已经到了最大,
这里继续对同一个迭代器进行遍历, 执行__next__()方法将直接抛出异
常, 结束循环 “”"
for j in cont_iterator:
print(j)  # 没有输出

print(cont_iterator.next())  # 抛出 StopIteration 异常

√ 如果只有 getitem() 方法:

class Container:
def __init__(self, iterable):
self.iterable = iterable
def __getitem__(self, index):
return self.iterable[index]
cont = Container([ 5 , 6 , 7 ])
"""
for语句在执行时, 先找__iter__()方法, 没有找到, 则找
__getitem__()方法, 然后
每循环一次, 调用一次__getitem__()方法, 其中index参数从 0 开始,
无限递增+1, 当
index超出iterable索引范围抛出IndexError, for语句会捕获这个异
常来break循环 """
for i in cont:
print(i)
""" list()、tuple()、set()、sum()等也是基于类似原理 """
print(list(cont))
print(tuple(cont))
print(set(cont))
print(sum(cont))
""" 测试__getitem__()方法中的index参数 """
class MyObject:

生成器

√ 生成器写法类似于标准的函数写法,不同点在于:

生成器用 yield 语句返回数据,而标准的函数用 return 语句返

回数据

yield 语句返回数据之后会挂起函数的状态,并会记住上次执

行语句时的所有数据值,方便每次在生成器调用__next__()方

法时,从上次挂起的位置恢复继续执行,而 return 语句返回

一次数据之后,函数就结束了

def __init__(self):
pass
def __getitem__(self, index):
if index > 3 :
raise IndexError
return index
mo = MyObject()
for i in mo:
print(i)
print(list(mo))
print(tuple(mo))
print(set(mo))
from collections import Generator, Iterator
def func():
print("The function body starts executing...")

return 2
print(“The function body continues execution…”)
return 4
print(“ending…”)

res = func()
print(res)

def gen():
print(“The function body starts executing…”)
yield 2
print(“The function body continues execution…”)
yield 4
print(“ending…”)

“”"

包含yield关键字的函数就是生成器函数, 调用生成器函数不会执行函数
体代码,
而是直接返回一个生成器对象, 调用__next__()方法才会开始执行函数
体代码 “”"
g = gen()  # 返回生成器对象
print(isinstance(g, Generator))  # True
print(“iter” in dir(g) and “next” in dir(g))  #
True: 生成器实现了迭代器协议
print(isinstance(g, Iterator))  # True: 生成器一定也是迭代

“”"

生成器对象调用__next__()方法就会开始执行函数体代码, 遇到 yield
则把

后面的数据返回, 然后挂起函数的状态, 直到下一次调用__next__()方
法, 再
从挂起的状态继续往后执行, next()方法如果没有可以返回的值,
会抛出
StopIteration异常, 直到函数体执行完毕, 函数才算结束 “”"
print(g.next())
print(g.next())

“”" 从上一步挂起状态继续往后执行, 输出: ending…
此时__next__()没有可以返回的值, 抛出 StopIteration 异常
函数体执行完毕, 函数结束 “”"

print(g.next())

g2 = gen()  # g迭代完毕, 再建一个生成器

“”"
生成器也是迭代器, 所以和迭代器逻辑是一样的, for语句在执行时会
先调用__iter__()方法, 返回self, 然后每次循环时, self调用一次
next()方法, 因为调用了该方法, 函数体就会开始执行, 执行到
yield, 把后面的数据返回, 然后函数挂起, 当循环又调用__next__()
方法时, 则从函数挂起处继续执行函数体, 而当没有 yield 时, 即没
有返回数据时, next()方法将引发 StopIteration 异常, for语
句会捕获这个异常来 break 循环 “”"
for i in g2:
print(i)

“”" list()、tuple()、set()、sum()等也是基于类似原理 “”"
print(list(g2))
print(tuple(g2))
print(set(g2))
print(sum(g2))

生成器表达式

生成器表达式所用语法类似列表推导式,只是外层为圆括号而非方括

生成器表达式相比完整的生成器函数来说更紧凑,相比列表推导式则

更为节省内存,因为列表推导式是一次构建一个结果列表,而生成器

表达式返回的是一个生成器,再根据对生成器的处理函数按需迭代产

生结果

# 列表推导式
list1 = [i for i in range( 5 )]
print(type(list1))  # <class 'list'>
print(sum(list1))  # 10
print(sum(list1))  # 10
# 生成器表达式
gt1 = (i for i in range( 5 ))
print(type(gt1))  # <class 'generator'>
"""
这里gt1第二次sum结果为什么是0, 而上面的list1却不是?
因为同一个gt1生成器对象, 第一次sum已经迭代完了, 而sum默认从 0 开
始累加, 结果就为 0
而每次sum(list1)都会通过list的__iter__()方法返回新的迭代器,
并不是同一个 """
print(sum(gt1))  # 10
print(sum(gt1))  # 0
""" 生成器表达式如果立即被外层的函数使用, 可以省略圆括号,
而不用写成 sum((i for i in range(5))) """
print(sum(i for i in range( 5 )))  # 10

iter(object[,sentinel])

返回一个迭代器对象

如果没有第二个实参,object必须支持迭代协议(有__iter__()方法)或

序列协议(有__getitem__()方法,且数字参数从 0 开始)。如果它不支

持这些协议,会触发TypeError

如果有第二个实参sentinel,那么object必须是可调用的(函数、方

法、lambda匿名函数、 类以及实现了 call ()方法的实例对象)。

这种情况下生成的迭代器,每次迭代调用它的__next__()方法时都会不

带实参地调用object,返回调用的结果,如果返回的结果是sentinel,

则触发StopIteration

str1 = "abc"
list1 = [ 1 , 2 , 3 ]
tup1 = ( 1 , 2 , 3 )
dict1 = {"one": 1 , "two": 2 , "three": 3 }
set1 = { 1 , 2 , 3 }
# str1、list1、tup1、dict1、set1 支持迭代协议
print(iter(str1))
print(iter(list1))
print(iter(tup1))
print(iter(dict1))
print(iter(set1))
class MyObject:
def __init__(self):
pass
def __getitem__(self, index):
pass

next(iterator[, default])

通过调用 iterator 的 next() 方法获取下一个元素。如果迭代器耗

尽,则返回给定的 default,如果没有默认值则触发 StopIteration

# MyObject() 支持序列协议
print(iter(MyObject()))
class MyObject1:
def __init__(self):
self.num = 3
def __call__(self):
self.num += 1
return self.num
“”"
有第二个参数 sentinel = 7, 且 MyObject1() 是可调用对象, 则每
次迭代时
调用 MyObject1()() 即调用 __call__方法, 当结果为 sentinel
时,
触发StopIteration, 被list捕获, 停止迭代 """
call_iter = iter(MyObject1(), 7 )
print(call_iter)
print(list(call_iter))  # [4, 5, 6]

迭代器的优缺点

迭代器优点 :

提供了一种不依赖索引的迭代取值方式

节省内存,迭代器在内存中相当于只占一个数据的空间:因为每次取

值上一条数据都会在内存释放,再加载当前的此条数据,而不需要一

次性把所有数据加载到内存当中

执行列表推导式:sum([i for i in range(10000000000)]) 的内存使用情

执行生成器表达式:sum(i for i in range(10000000000)) 的内存使用情

str_iterator = iter("abcd")
print(next(str_iterator))  # "a"
print(next(str_iterator))  # "b"
print(next(str_iterator))  # "c"
print(next(str_iterator))  # "d"
print(next(str_iterator, "ef"))  # 迭代耗尽, 返回 "ef"
print(next(str_iterator))  # 迭代耗尽, 触发 StopIteration

迭代器缺点 :

取值不如按照索引的方式灵活,不能取指定的某一个值,只能往后

取,不能往前去

除非取尽,否则无法获取迭代器的长度

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值