第一部分 迭代器
"""
迭代器
"""
li=[1,2,3,4] tu=(1,2,3,4) s="abc" b=b"def" d={"a":1,"b":2} se={1,2,3} for i in li: pass for i in tu: pass for i in s: pass for i in b: pass for i in d: pass for i in se: pass # for i in 1: # pass
1. 可迭代对象
# 能够放在for循环中进行遍历的类型,就是可迭代类型,产生的对象就是可迭代对象
# 可迭代对象类型,在python collections.abc.Iterable,包括__iter__方法。所有的可迭代对象。
# 必须都实现__iter__方法。返回的是迭代器。
# 学过的list,tuple等可迭代对象都是继承了这个父类Iterable,实现__iter__
from collections.abc import Iterable print(issubclass(list,Iterable)) print(issubclass(tuple,Iterable)) print(issubclass(str,Iterable)) print(issubclass(bytes,Iterable)) print(issubclass(dict,Iterable)) print(issubclass(set,Iterable)) print(issubclass(int,Iterable))
# 说明:可迭代对象有一些没有实现__iter__方法,是特殊的,而是实现的__getiterm__方法
2. 迭代器
# 迭代器是迭代对象的特殊类型,迭代器是迭代对象的子类
from collections.abc import Iterator
print(issubclass(Iterator,Iterable))
# 在迭代器中两个重要方法 __next__ __iter__
# __next__方法:能够返回迭代器下一个元素
li=[1,2,3] # li是迭代对象
# __iter__方法:能够返回迭代器
# li.__iter__() iter(li)在调用上等价,但是建议用后一种方法
# it=iter(li)
# it 是迭代器,迭代器中包含了所有的元素
it=iter(li)
# it.__next__() next(it)在调用上等价,建议用后一种
print(it.__next__())
print(next(it))
print(next(it))
print(next(it)) # 当迭代器中的元素已经全部被next获得之后,会报StopIteration的异常
# 迭代器产生一次,只要遍历过,当前的迭代器中的元素就没有了。
# for 循环是怎么做到可以一直遍历迭代对象的呢?
# 是因为每次for的时候,都会新创建一个迭代器,进行迭代
li1=[1,2,3,4,5]
# it=li1.__iter__()
# 获得当前迭代对象的迭代器(这个迭代器一次只产生一个)
it=iter(li1)
while True:
try:
item=next(it)
except StopIteration:
del it
break
# 注意:
# 1. 分清迭代对象是什么?迭代器是什么?
# Iterable 迭代对象,我们学过的所有的list,tuple都是Iterable的子类
# 如果新创建一个列表,是一个对象,是list的对象,list是Iterable的子类
# 2. 他们之间的关系
# Iterator,是迭代对象的子类
# 3. 迭代对象下有什么方法?干什么的?
# # Iterable下有__iter__方法,注意,调用的时候,可以直接使用__iter__,建议使用iter(迭代对象)
# iter 方法能够返回一个迭代器。
# 4. 迭代器下有什么方法?干什么的?
# Iterator 有__next__:能够找到迭代器中的下一个对象。
# 还有 __iter__:return self。 为了扩展使用
# 注意,迭代器在遍历一次之后,迭代器的元素就没了,如果需要再次遍历,需要重新使用迭代对象
# 调用迭代对象下的iter方法创建新的迭代器。
3. 自定义迭代对象和迭代器。
# 符合如下规则:
迭代对象: 里面实现__iter__方法。
迭代器: 里面实现__iter__和__next__方法
class MyIterable:
def __iter__(self):
pass
class MyIterator:
def __iter__(self):
pass
def __next__(self):
pass
# 只要定义的迭代对象,实现了__iter__方法,那么它就是迭代对象,就是Iterable的子类,不需要显示继承
# 只要定义了迭代器,实现了__next__ 和__iter__,那么它就是迭代器,就是iterator的子类,不需要显示继承
m=MyIterable() print(isinstance(m,Iterable)) print(issubclass(MyIterable,Iterable)) n=MyIterator print(isinstance(n,Iterator)) print(issubclass(MyIterator,Iterator)) class MyIterable1: def __iter__(self): self.li=[1,2,3,"a"] return class MyIterator1: def __init__(self,li): self.li=li self.index=0 def __iter__(self): pass def __next__(self): if self.index<len(self.li): s=self.li[self.index] self.index+=1 return s else: # 当索引大于长度 raise StopIteration m=MyIterable() m1=iter(m) print(m1.__next__()) print(next(m1))
# 为什么list 或者tuple这些可迭代的数据类型被设计成“迭代对象”,而不是直接设计成“迭代器"?
# 迭代器
# 写一个迭代器,实现一个倒数的迭代器
4. 迭代器的缺点
# (1) 必须要去实现iter方法和next方法
# (2) 如果不是用for 循环,那么无返回值的时候,需要捕捉StpIteration的异常
# (3) 一次性全部迭代,结果未必是一下子全部使用,当数量级太大的时候,会占用太多的内存
# 解决方法-------生成器
第二部分 生成器
"""
生成器
在python2.5之后出现的,生成器返回一个可迭代的对象,但是不是立即返回所有的值,而是一个一个的生成值
懒加载:延迟加载,按需加载。
生成器,其实就是给一个函数,生成一个懒加载的迭代器。
"""
# 将列表中的每一个元素的数,求平方
li=[]
for i in range(1,101):
li.append(i**2)
print(li)
print([i**2 for i in range(1,101)])
# 生成器的实现有两种方式:一种生成器表达式,另一种是生成器函数
1. 使用生成器表达式
linew=(i**2 for i in range(1,101))
# linew是一个生成器对象
print(linew)
# 因为生成器底层是迭代器,使用for 循环,可以触发生成器产生每一个元素
# for i in linew:
# print(i)
# 生成器就是迭代器,遍历完一次之后就没有东西了
print(next(linew))
2. 生成器函数
生成器函数就看有没有yield
# 定义一个普通函数
# def f():
# for i in range(1,101):
# print(i)
# f()
# 生成器函数
def f():
print("生成器函数开始执行")
for i in range(1,101):
yield i
print(i,"暂停之后继续执行")
a=f()
print(a)
a.__next__()
next(a)
next(a)
for i in a:
print(i)
# 练习:使用正常函数和生成器函数写出斐波那契数列
def f():
print("程序开始执行")
a=0
b=1
for i in range(100):
print("yield之前")
yield b
print("yield之后继续执行")
a,b=b,a+b
g=f()
print(next(g))
print(next(g))
print(next(g))
print(next(g))
# 说明: 1. 生成器函数,在被调用的时候,只会产生一个生成器对象(本质迭代器对象)
# 2. 不会运行函数中的任何一行代码
# 3. 当使用next(生成器对象),才触发函数执行,运行到yield暂停
# 4. 当继续调用next函数时,从刚才暂停的位置,继续向后执行
"""
生成器函数和普通函数的区别
1. 生成器函数会包含一个或者多个yield
2. 当生成器函数运行到yield,暂停,相当于把控制权交给调用者
"""
3. yield表达式。
# 在生成器函数除了可以使用yield产生一个值以外,其实,同时yield表达式本身还具有一个值,
# 不是产出的值,而是使用send方法传递进去的值
# send(值)
def fun():
for i in range(100):
value=yield i
print(value)
g=f()
print(g.send(None))
print(g.send("send传入的值"))
print(next(g))
print(g.send("这是send传进来的值"))
print(g.send(None))
# send 和 next 他们都可以调用生成器对象产生的元素
# 调用next函数,相当于传入值None,next函数的返回值是yield生成的内容
# 调用send函数,可以传入yield表达式的值(生成器函数中value值,value=yield i)
# send函数的返回值,也是yield生成的内容
# 第一次调用next唤醒生成器对象,跟使用send(None)效果一样
# 第一次调用只能使用send(None),分析原因,停在yield,还没有对左侧的value进行赋值
# 需求:使用生成器产生一系列的值,比如,1,2,3..到5时,返回给客户端,说太大了,变小点
# 生成器在继续产生4,3,2,1,到1的时候,客户端说太小了,变大点,生成器继续产生2,3,4,5
第三部分 闭包
"""
闭包:
在内部函数中,访问外部函数的变量,在外部函数中,直接返回内部函数引发调用
(1) 有嵌套函数
(2) 内部函数可以访问到外部函数的变量
(3) 外部函数返回内部函数的名字
"""
def abc(): print("已经存在的函数") def outer(a): x=1 def inner(): print(x) print(a) print("inner函数") return inner inner=outer("abc") print(inner.__closure__) inner()
# 闭包的作用:
# 1. 当函数执行结束时,内部函数依然可以引用到外部函数中定义的变量,下一次再调用内部函数时
# 依然可以调用到外部函数中变量值。
# 2. 内部函数的名称处于局部命名空间,不会对外部全局命名空间造成影响,即使函数名相同也没问题
# 实现bill第几天上班
def on_duty(name): times=0 times+=1 print("这是{}第{}次上班".format(name,times)) on_duty("bill") on_duty("bill") # 闭包 def on_duty(name): times=0 def inner(): nonlocal times times+=1 print("这是{}第{}次上班".format(name,times)) return inner duty=on_duty("bill") duty() duty() duty()
# 通过类也能实现闭包
class Onduty: def __init__(self,name): self.name=name self.times=0 def __call__(self, *args, **kwargs): self.times+=1 print("这是{}第{}次上班".format(self.name,self.times)) duty=Onduty("bill") duty() duty()