python学习 day4
一、装饰器
装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,
装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试事务
处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离大量与函数
功能本身无关的雷同代码并继续重用。在原有的函数前后增加功能,且不改变原函数的调用方式
# 计算一个函数的运行之间 import time def timmer(f): def inner(*args,**kwargs): start_time = time.time() ret = f(*args,**kwargs) end_time = time.time() print(end_time - start_time) return ret return inner @timmer # func = timmer(func) def func(a,b): print('begin func',a) time.sleep(0.1) print('end func',b) return True
带参数的装饰器
# 进阶的需求
# 第一种情况
# 500个函数
# 你可以设计你的装饰器 来确认是否生效
import time FLAG = True def outer(flag): def timmer(f): def inner(*args,**kwargs): if flag == True: 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 timmer @outer(FLAG) # func = timmer(func) def func(a,b): print('begin func',a) time.sleep(0.1) print('end func',b) return True func(1,2)
多个装饰器装饰同一个函数:(多个装饰器执行原则是从上至下执行,然后从下至上返回函数执行结果)
def wrapper1(func): def inner(): print('wrapper1 ,before func') func() print('wrapper1 ,after func') return inner def wrapper2(func): def inner(): print('wrapper2 ,before func') func() print('wrapper2 ,after func') return inner @wrapper2 @wrapper1 def f(): print('in f') f()
二、迭代器
如何从列表、字典中取值的
index索引 ,key
for循环
凡是可以使用for循环取值的都是可迭代的
可迭代协议 :内部含有__iter__方法的都是可迭代的
迭代器协议 :内部含有__iter__方法和__next__方法的都是迭代器
print(dir([1,2,3]))
结果: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。
print([1,2,3].__iter__()) 结果: <list_iterator object at 0x05285670>
执行了list([1,2,3])的__iter__方法,我们好像得到了一个list_iterator ---> 迭代器
{'__length_hint__', '__next__', '__setstate__'} 迭代器多了这三个内置方法 iter_l = [1,2,3,4,5,6].__iter__() #获取迭代器中元素的长度 print(iter_l.__length_hint__()) #根据索引值指定从哪里开始迭代 print('*',iter_l.__setstate__(4)) #一个一个的取值 print('**',iter_l.__next__()) print('***',iter_l.__next__())
模拟for循环进行取值
l = [1,2,3,4] l_iter = l.__iter__() item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) item = l_iter.__next__() print(item) #这是一段会报错的代码,如果我们一直取next取到迭代器里已经没有元素了,就会抛出一个异常StopIteration,告诉我们,列表中已经没有有效的元素了。
可以使用python中异常处理模块
l = [1,2,3,4] l_iter = l.__iter__() while True: try: item = l_iter.__next__() print(item) except StopIteration: break
注:for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的__iter__方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象就都可以通过for循环来遍历了
三、生成器
在工作过程中,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的东西就叫生成器。
Python中提供的生成器:
1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器Generator:
本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)
特点:惰性运算,开发者自定义
生成器函数:
一个包含yield关键字的函数就是一个生成器函数。yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象。每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值。直到函数执行结束。
# 生成器函数的调用不会触发代码的执行,而是会返回一个生成器(迭代器) # 想要生成器函数执行,需要用next def cloth_g(num): for i in range(num): yield 'cloth%s'%i g = cloth_g(1000) print(next(g)) print(next(g)) print(next(g)) # 使用生成器监听文件输入的例子 import time def listen_file(): with open('userinfo') as f: while True: line = f.readline() if line.strip(): yield line.strip() time.sleep(0.1) g = listen_file() for line in g: print(line)
send关键字:
想生成器中传递值,有一个激活的过程,第一次必须要用next触发这个生成器。
def generator(): print(123) content = yield 1 print('=======',content) print(456) yield 3 g = generator() ret = g.__next__() print('***',ret) ret = g.send('hello') #send的效果和next一样 print('***',ret) #send 获取下一个值的效果和next基本一致 #只是在获取下一个值的时候,给上一yield的位置传递一个数据 #使用send的注意事项 # 第一次使用生成器的时候 是用next获取下一个值 # 最后一个yield不能接受外部的值
列表推导式和生成器表达式
egg_list=['鸡蛋%s' %i for i in range(10)] #列表解析 laomuji=('鸡蛋%s' %i for i in range(10))#生成器表达式 print(laomuji) print(next(laomuji)) #next本质就是调用__next__ print(laomuji.__next__()) print(next(laomuji))
# 如何从生成器中取值
# 第一种 :next 随时都可以停止 最后一次会报错
# print(next(g))
# print(next(g))
# 第二种 :for循环 从头到尾遍历一次 不遇到break、return不会停止
# for i in g:
# print(i)
# 第三种 :list tuple 数据类型的强转 会把所有的数据都加载到内存里 非常的浪费内存
# print(g)
# print(list(g))
# 生成器函数 是我们python程序员实现迭代器的一种手段
# 主要特征是 在函数中 含有yield
# 调用一个生成器函数 不会执行这个函数中的带码 只是会获得一个生成器(迭代器)
# 只有从生成器中取值的时候,才会执行函数内部的带码,且每获取一个数据才执行得到这个数据的带码
# 获取数据的方式包括 next send 循环 数据类型的强制转化
# yield返回值的简便方法,如果本身就是循环一个可迭代的,且要把可迭代数据中的每一个元素都返回 可以用yield from
# 使用send的时候,在生成器创造出来之后需要进行预激,这一步可以使用装饰器完成
# 生成器的特点 : 节省内存 惰性运算
# 生成器用来解决 内存问题 和程序功能之间的解耦
总结:
1.把列表解析的[]换成()得到的就是生成器表达式
2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
3.Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:
sum(x ** 2 for x in range(4))