做企业网站多,沈阳男科医院咨询电话,企业网站建设图片,编程网址可迭代对象
定义
在Python的任意对象中#xff0c;只要它定义了可以返回一个迭代器的 __iter__ 魔法方法#xff0c;或者定义了可以支持下标索引的 __getitem__ 方法#xff0c;那么它就是一个可迭代对象#xff0c;通俗的说就是可以通过 for 循环遍历了。Python 原生的列…可迭代对象
定义
在Python的任意对象中只要它定义了可以返回一个迭代器的 __iter__ 魔法方法或者定义了可以支持下标索引的 __getitem__ 方法那么它就是一个可迭代对象通俗的说就是可以通过 for 循环遍历了。Python 原生的列表字符串元祖字典等都是可以迭代的可以通过 dir() 函数查看如以下查看字符串可见其定义了 __getitem__ 方法 判断一个对象是否是可迭代对象
方法一isinstance Iterable
方法二hasattr __getitem__
from collections import Iterable
# 方法一
print(isinstance([], Iterable)) # 返回 True说明是可迭代对象
# 方法二
print(hasattr({}, __getitem__)) # 返回 True说明是可迭代对象
自定义可迭代对象
class Employee:def __init__(self, employee):self.employee employeeemp Employee([zs, ls, ww])# 正常情况下会报错emp 不是一个可迭代对象
# TypeError: Employee object is not iterable
for i in emp:print(i) 实现 __iter__ 和 __getitem__ 方法中的一个或者两个
class Employee:def __init__(self, employee):self.employee employeedef __getitem__(self, item):# item 是解释器帮我们维护的索引值# 在 for 循环中自动从 0 开始计算print(item)return self.employee[item]emp Employee([zs, ls, ww])# 正常情况下会报错emp 不是一个可迭代对象
# TypeError: Employee object is not iterable
for i in emp:print(i)
迭代器
迭代器对象
迭代器就是同时实现了 __next__ 和 __iter__ 方法缺一不可的对象。其中 __iter__ 方法返回迭代器自身 __next__ 方法不断返回迭代器中的下一个值直到容器中没有更多的元素时抛出 StopIteration 异常以终止迭代 由定义可以知道迭代器一定是可迭代对象因为迭代器实现了 __iter__ 方法满足了可迭代对象的定义。但是可迭代对象不一定是迭代器因为不一定实现了 __next__ 方法。
判断一个对象是迭代器
from collections import Iterator
print(isinstance(counter, Iterator)) # True
print(isinstance([], Iterator)) # False
为什么有了可迭代对象还要有迭代器
因为迭代器采用了工厂模式节约了内存空间。所谓的工厂模式就是在需要的时候才会去生产数据而不是像可迭代对象那样一次性 全部把数据生产出来。可迭代对象如列表字典等会事先把所有的数据生产并保存起来而迭代器则是在每次获取下一个值的时候 才会返回值所以迭代器没有长度这一说法没有长度这一属性如果获取完了会抛出 StopIteration 异常来表示没有数据了
所以迭代器适用于那种无限序列不会占用很大的内存空间
from itertools import count# count 是一个迭代器
# 创建一个从 10 开始的无限序列
counter count(start10)
print(type(counter)) # class itertools.count
print(dir(counter)) # 有 __iter__ 和 __next__ 方法print(next(counter)) # 10
print(next(counter)) # 11# 报错没有长度属性
# TypeError: object of type itertools.count has no len()
# print(len(counter))
可迭代对象转为迭代器
a [1, 2, 3, 4]
print(type(a)) # class lista_iter iter(a)
print(type(a_iter)) # class list_iterator
迭代器的特点
a [1, 2, 3, 4]
a_iter iter(a)
# 每一次获取迭代器中的值之后都是把该值从迭代器中拿出来即迭代器中已经没有该值了因为已经被拿出来了
# 所以遍历完一次迭代器之后不能遍历第二次
# 因为遍历第一次的时候已经把值拿出来完了第二次去遍历的时候迭代器中啥也没有了
# 即迭代器不走回头路可迭代对象则没有这种特点
for i in a_iter:print(i)for i in a_iter:print(i)# 上面代码只会输出一次 1 2 3 4# 前面说的当遍历完迭代器的最后一个数据时再获取数据抛出异常
# 而 for 循环没有抛出的原因是
# for 循环内部也是用 next() 方法获取迭代器中的值
# 当抛出异常后 for 循环会对异常进行处理所以我们采用 for 循环遍历迭代器时不会有异常抛出
生成器
生成器其实是一种特殊的迭代器但是这种迭代器更加优雅因为不需要再像普通迭代器那样定义 __next__ 和 __iter__ 方法只需要一个 yield 关键字就会自动在内部帮我们实现这两个方法。所以如果一个函数包含一个或多个 yield 关键字 这个函数就会变为一个生成器。因为生成器是特殊的迭代器所以它具备迭代器具备的特性
def demo():print(hello)yield 5print(world)print(type(demo())) # class generator
print(dir(demo)) # 有 __iter__ 和 __next__ 方法
yield 关键字的作用
1、程序每次在代码中遇到 yield 关键字后会返回结果相当于 return 5但是并没有真的退出程序而是保留当前函数的运行状态
2、返回结果后保留当前函数的运行状态等待下一次调用下次调用时从上一次返回结果处开始执行
第二个作用非常重要这意味着程序控制权的转移是临时和自愿的函数将来还会收回控制权在下次调用生成器的时候收回 这也是 yield 和 return 最大的区别。return 意味着函数彻底交出控制权并结束运行下一次调用将固定从函数的第一行代码开始执行。
def demo():print(hello)yield 5print(world)# 此时运行demo()相当于调用函数但是并不像普通函数那样马上执行可以看控制台没有输出
# 此处只是生成一个生成器对象
c demo()# 利用 next 方法调用生成器
# 第一次调用打印 hello 并返回 5
print(next(c))
# 第二次调用打印 world 并抛出异常 StopIteration迭代器特性
# 因为已经没有语句可以执行了相当于数据已经取完了
print(next(c))
通过 send 向生成器传递数据
send 方法作用
1、像 next 方法一样去调用生成器调用生成器的两种方法next 方法和 send 方法
2、send 方法在调用生成器时可以同时给生成器传递数据到生成器内部
def demo():print(hello)# 注意此处不是把 yield 5 赋值给变量 t# yield 5 是返回给调用者的值即返回给 next(c)# 所以执行 print(next(c)) 语句时会在输出 hello 后输出 5# 而变量 t 是接收下一次调用c.send(test)时 send 方法传入的 test# 即 t testt yield 5print(world)print(t)c demo()
# 第一次调用生成器得到 t yield 5 等号左边的表达式结果即得到 5
print(next(c))
# 第二次调用从 t yield 5 语句开始执行通过 send 方法把 test 传递给 t yield 5 等号右边的变量 t
# 然后接着执行函数中两个打印语句输出 world 和 test
# 打印之后就会抛出异常 StopIteration因为已经没有要执行的语句了
c.send(test)
生成器的预激活机制
def demo():print(hello)t yield 5print(world)print(t)c demo()
# next(c) 就是生成器的预激活机制即第一次调用
# 第一次调用也可以通过 send 方法但是参数必须为 None
# 因为生成器没有办法在没有激活的情况下接收一个参数
# print(next(c)) # 预激活方式一
c.send(None) # 预激活方式二
c.send(test)查看生成器的运行过程
将下面代码的每一行打上断点如图进入debug模式可以清晰的看到代码的运行过程。 def countdown(n):print(counting down from , n) # 只在第一次调用生成器的时候输出此时 n 10while n 0:# 记住这里不是将 yield n 赋值给变量 newvalue# newvalue 是用来接收 send 方法传递过来的参数的# yield n 是用来将 n 返回给调用者next 方法或 send 方法的newvalue yield n# 判断是否用 send 方法传递参数如果调用 next 方法则传递过来的是 Noneif newvalue is not None:n newvalueelse:n - 1# 获得生成器对象 c把 10 传递给 n
c countdown(10)for i in c:# 第一次调用生成器# 第一次执行 for i in c 语句时内部第一次调用 next(c)# 执行 print(counting down from , n) 语句此时 n10# 然后进入 while n 0 循环执行 newvalue yield n 等号右边的 yield n即返回 n10 给i# 所以第一次for循环 i10输出 10# 第三次调用生成器# 第二次执行 for i in c 语句时内部第二次调用 next(c)此时已经第三次调用生成器第二次是用 send 方法调用的# 接着第二次调用生成器的地方开始执行即从 newvalue yield n 开始执行next 方法没有给生成器传递参数# 所以 newvalue 为 None则执行 else 语句n - 1所以 n 2# 进入 while n 0 然后返回newvalue yield n 等号右边的 yield n即返回 n2# 所以第二次for循环 i2输出 2# 第四次调用生成器# 第三次执行 for i in c 语句时内部第三次调用 next(c)# 接着第三次调用生成器的地方开始执行即从 newvalue yield n 开始执行next 方法没有给生成器传递参数# 所以 newvalue 为 None则执行 else 语句n - 1所以 n 1# 进入 while n 0 然后返回newvalue yield n 等号右边的 yield n即返回 n1# 所以第三次for循环 i1输出 1# 第五次调用生成器# 第四次执行 for i in c 语句时内部第四次调用 next(c)# 接着第四次调用生成器的地方开始执行即从 newvalue yield n 开始执行next 方法没有给生成器传递参数# 所以 newvalue 为 None则执行 else 语句n - 1所以 n 0# 进入 while n 0 然后返回newvalue yield n 等号右边的 yield n即返回 n0# 所以第四次for循环 i0输出 0# 第六次调用生成器最后一次# 第五次执行 for i in c 语句时内部第五次调用 next(c)# 接着第五次调用生成器的地方开始执行即从 newvalue yield n 开始执行next 方法没有给生成器传递参数# 所以 newvalue 为 None则执行 else 语句n - 1所以 n -1# 不能进入 while n 0 那么生成器运行到最后结束抛出一个异常 StopIteration# 抛出的异常被for循环内部处理所以运行到此结束for循环也结束print(i) # 分别输出 10 2 1 0# 第一次进入 for 循环时 i 10所以进入 if 判断if i 10:# 第二次调用生成器# 调用 send 方法调用生成器接着第一次调用的地方继续执行# 把参数传递给 newvalue yield n 中的 newvalue此时 newvalue3# newvalue 不为 None进入判断 if newvalue is not None将 n 设置为 newvalue n newvalue# 此时 n 3进入 while n 0 然后返回newvalue yield n 等号右边的 yield n即返回 n3# 然后进行第二次 for 循环print(send: , c.send(3)) # 打印 send: 3
获得生成器的第二种方式
除了通过 yield 关键字得到生成器外还可以通过小括号的形式得到如下 生成器表达式和列表推导式的区别列表推导式会一下子将所有的数据生产出来并放到列表中生成器表达式一次只生产一个数据获得生成器的两种方式1、将普通函数里的 return 替换成 yield 这样调用函数时会得到一个生成器2、利用生成器表达式
# 可以通过元祖表达式小括号得到生成器
a (i for i in range(5))
# 列表推导式得到的是一个列表
b [i for i in range(5)]# a 是生成器
print(type(a)) # class generator
# b 是列表
print(type(b)) # class list# 利用推导式获得生成器的方式称为生成器表达式
t (i * 2 for i in range(5))
print(t) # generator object genexpr at 0x000001F466254620
print(next(t)) # 0
print(next(t)) # 2
# 把剩下的数据处理目前只剩下 2 3 4
for i in t:print(i) # 4 6 8
生成器实现斐波那契数列
# 用生成器实现斐波那契数列
def fib():num1, num2 0, 1while True:yield num1num1, num2 num2, num1 num2f fib()
# 因为 fib 函数里是死循环所以只要调用 next(f) 就可以一直得到 斐波那契数列的值
print(next(f)) # 0
print(next(f)) # 1
print(next(f)) # 1
print(next(f)) # 2
print(next(f)) # 3
print(next(f)) # 5