装饰器进阶
带参数的装饰器
# 某一种情况
# 500个函数加装饰器, 加完后不想再加这个装饰器, 再过一个季度,又想加上去
# 你可以设计你的装饰器,来确认是否执行
# 第一种情况 # 想要500个函数执行装饰器的话,可以定义个变量flag # Flag = False # import time # def outer(flag): # def timer(f): # def inner(*args, **kwargs): # if flag == True: #如果flag==ture,装饰器函数执行 # start_time = time.time() # ret = f(*args, **kwargs) # end_time = time.time() # print(end_time - start_time) # else: #否则不执行 # ret = f(*args, **kwargs) # return ret # return inner # return timer # # @outer(Flag) #最核心的地方,先执行outer(flag)== timer, 相当于@timer # def func(a, b): # time.sleep(0.1) # print(666) # func(1, 2)
多装饰器装饰同一个函数
# 第二种情况,多装饰器装饰一个函数
# 装饰器 登录 记录日志
# login_info = {'alex': False} # def wrapper1(func): #3 func = f # def inner1(): # print('wrapper1 ,before func') #14 # func() #15 f() # print('wrapper1 ,after func') #17 # return inner1 #4 返回inner1 # # def wrapper2(func): #7 func = inner1, # def inner2(): #11 # print('wrapper2 ,before func') #12 # func() #13 inner1() # print('wrapper2 ,after func') #18 # return inner2 #8 return inner2 # # @wrapper2 #6 f = wrapper2(f'-->inner1') = wrapper1(inner1) 9 f = inner2 # @wrapper1 #2 f = wrapper1(f) 5 f = inner1 # def f(): #1 # print('in f') #16 # # f() #10 inner2()
生成器和迭代器
迭代器
python中的for循环
for i in [1,2,3,4]: print(i)
换一种情况
for i in 1234: print(i) TypeError: 'int' object is not iterable
说int类型不是一个iterable
迭代和可迭代协议
字符串、列表、元组、字典、集合都可以被for循环,说明他们都是可迭代的。
可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代
可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了__iter__方法。
想要可迭代的内部必须有个__iter__方法
__iter__ 都做了些什么
print('asdasd'.__iter__())
结果: # <str_iterator object at 0x00000000021E7320>
通过 __iter__ 方法,我们得到了个iterator对象
iterator 叫做迭代器
# print(dir([1, 2, 3, 4].__iter__)) ''' ['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__objclass__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__text_signature__'] '''
迭代器协议
内部含有__iter__和__next__方法的都是迭代器
迭代器内部多了个__next__() 方法
l1_iter = [1, 2, 3, 4].__iter__() print(l1_iter.__next__()) print(l1_iter.__next__()) print(l1_iter.__next__()) print(l1_iter.__next__()) print(l1_iter.__next__()) #StopIteration # 结果: StopIteration 1 2 3 4
我们一直取next取到迭代器里已经没有元素了,就会抛出一个异常StopIteration,告诉我们,列表中已经没有有效的元素了。
这个时候,我们就要使用异常处理机制来把这个异常处理掉
l = [1,2,3,4] l_iter = l.__iter__() while True: try: item = l_iter.__next__() print(item) except StopIteration: break
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
range()
# py2 range,不管range多少, 会生成一个list,这个列表存储所以值
# py3 range, 不管range多少,都不会生成实际的任何一个值,只有在找他要值的时候才会没要一个值,生成一个值
print('__next__' in dir(range(12))) #查看'__next__'是不是在range()方法执行之后内部是否有__next__ print('__iter__' in dir(range(12))) #查看'__next__'是不是在range()方法执行之后内部是否有__next__ from collections import Iterator print(isinstance(range(100000000),Iterator)) #验证range执行之后得到的结果不是一个迭代器
# __next__() == next()
# __iter__() == iter()
# 坑 # l = [1, 2, 3] # while True: # l1 = iter(l) # print(next(l1)) # while 每次循环都会生成一个新的迭代器对象,所以每次取值都是第一值
# 迭代器节省内存的例子 ''' f = open() for i in f: print i g.close() '''
# 列表, 字典, 元组, 字符串, 集合,range,文件句柄
# 可要是可迭代的对象,就可以通过iter方法转换为迭代器
生成器
# 自己写的迭代器,就是一个生成器
# 两种方法写生成器(迭代器)的机制:生成器函数, 生成器表达式
Python中提供的生成器:
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列
生成器Generator:
本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
生成器函数
一个包含yield关键字的函数就是生成器函数,yield可以为我们从函数中返回值,但是yield又不同于return
调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,
就能推动函数的执行,获取新的返回值。直到函数执行结束。
# def func(): # print(666) # yield 1 # print(func()) # --><generator object func at 0x0000000002670C50> # 生成器函数的调用不会触发代码的执行,而是返回一个生成器(迭代器) # g = func() # next(g) #-->666 # def func(): # print(666) # yield 1 # print(777) # yield 2 # # g = func() # print('--', next(g)) # print('--', next(g)) # yield记录当前所在的位置, 等待下次next来触发函数的状态 # 想要生成器函数执行,需要用next
更多应用
# 使用生成器监听文件输入的例子 # import time # def listen_file(): # with open('11.txt') as f: # while True: # line = f.readline() #这个位置不用fot循环的原因,是因为for知道文件什么时候结束,readline不知道文件结束,会一直去读 # if line.strip(): # yield line # time.sleep(0.1) # # g = listen_file() # for line in g: # print(line)
send关键字
# 在执行next的过程中,传递一个参数给生成器函数的内部 # 向生成器中传递值,有一个激活的过程,第一次必须要用next触发这个是生成器 # def func(): # print(66) # ret1 = yield 1 # print(77, 'ret1:', ret1) # ret2 = yield 2 # print(88, 'ret2:', ret2) # ret3 = yield 3 # g = func() # ret = next(g) # print(ret) # g.send('alex') # g.send('jin-xin') # g.send('jign') #--》StopIteration
# def average(): # sum_money = 0 # day = 0 # avg = 0 # while True: # money = yield avg # sum_money += money # day += 1 # avg = sum_money / day # # g = average() # next(g) # print(g.send(200)) # print(g.send(300)) # print(g.send(500))
# 预激生成器 # 计算移动平均值 # def average(): # sum_money = 0 # day = 0 # avg = 0 # while True: # money = yield avg # sum_money += money # day += 1 # avg = sum_money / day # # g = average() # next(g) # 预激活 # print(g.send(200)) # print(g.send(300)) # print(g.send(500)) # 目前有个需求省略与激活这一步 # def init(func1): # def inner(*args, **kwargs): # ret = func1(*args, **kwargs) # next(ret) # return ret # return inner # # @init # def average(): # sum_money = 0 # day = 0 # avg = 0 # while True: # money = yield avg # sum_money += money # day += 1 # avg = sum_money / day # # g = average() # print(g.send(200)) # print(g.send(300)) # print(g.send(500))
列表推导式和生成器表达式
# yield from py3新加的为了简化代码 # 如何从生成器中取值 ''' def generator_func(): for i in range(5): yield i for j in 'hello': yield j ''' # 等价于 ''' yield from range(5) == for i in range(5):yield i yield from 'hello' == for j in 'ghello':yield j ''' def generator_func(): yield from range(5) yield from 'hello'
# g = generator_func() # 第一种取值方式 next # 随时都可以停止,最后一次会报错 # print(next(g)) # 第二种方式 for循环 # 从头到尾遍历一次,不遇到break,return 不会停止 # for i in g: # print(i) # 第三种,list ,tuple # 该方式会一次性取出所有值,该方法不太好 # print(list(g)) # print(tuple(g))
# 踩到的坑 # g1 = generator_func() # g2 = generator_func() # print(g1, g2) ''' <generator object generator_func at 0x0000000002140C50> <generator object generator_func at 0x0000000002140D00> 可以看出每次生成一个新的生成器对象,所以直接用generator_func()取值的话会有问题
列表推导式
# 找到name中含有两个e的名字 # names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'], # ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']] # print([ n for n_list in names for n in n_list if n.count('e') == 2])
生成器表达式
# g = (i for i in range(30) if i %3 ==0) # print(g) # --><generator object <genexpr> at 0x0000000002670C50>
总结:
1.把列表解析的[]换成()得到的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
生成器相关的面试题
# 关于生成器的面试题 # def add(n, i): # return n + i # def test(): # for i in range(4): # yield i # # g = test() # for n in [1, 10]: # g = (add(n, i) for i in g) ''' # n=1 # g = (add(10, i) for i in for i in [0,1,2,3]) # n = 10 # g = (add(10, i) for i in add(10, i) for i in [0,1,2,3]) # g = (add(10, i) for i in [10,11,12,13]) # g = (20,21,22,23) ''' # print(list(g)) def add(n, i): return n + i def test(): for i in range(4): yield i g = test() for n in [1,2,10]: g = (add(n, i) for i in g) ''' n=1 g = (add(1, i) for i in test()) n=2 g = (add(2, i) for i in (add(2, i) for i in test())) n=10 g = (add(10, i) for i in (add(10, i) for i in (add(10, (0,1,2,3)))) ) n=10 g = (add(10, i) for i in (add(10, (10,11,12,13))) ) n=10 g = (add(10, (20,21,22,23))) ) n=10 g = (30,31,32,33)) ) ''' print(list(g))
def demo(): for i in range(4): yield i g=demo() g1=(i for i in g) #已经取完g中的值 g2=(i for i in g1) #取的空列表 print(list(g1)) print(list(g2)) ''' 结果: [0, 1, 2, 3] [] '''
import os def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper @init def list_files(target): while 1: dir_to_search=yield for top_dir,dir,files in os.walk(dir_to_search): for file in files: target.send(os.path.join(top_dir,file)) @init def opener(target): while 1: file=yield fn=open(file) target.send((file,fn)) @init def cat(target): while 1: file,fn=yield for line in fn: target.send((file,line)) @init def grep(pattern,target): while 1: file,line=yield if pattern in line: target.send(file) @init def printer(): while 1: file=yield if file: print(file) g=list_files(opener(cat(grep('python',printer())))) g.send('/test1') 协程应用:grep -rl /dir tail&grep
小结:
# 一个生成器只能取一次
# 生成器在不找它要值的时候,始终不执行
# 当他执行的时候,要以执行时候的所以变量值为准