python开发之装饰器&迭代器&生成器
闭包的引入
闭包:首先必须是嵌套函数,再次内部函数调用外部函数的变量,如下代码
def outer():
a = 1
def inner():
print("内部函数>",a)
print(inner.__closure__) # 用来判断闭包的,(<cell at 0x004573D0: int object at 0x53416880>,)
outer()
外部函数的返回值是内部函数的函数名
def outer():
a = 1
def inner():
print("内部函数>",a)
return inner
res = outer()
res()
闭包的小小应用:
from urllib.request import urlopen
def get_url():
url = 'http://www.xiaohua100.cn/index.html'
def inner():
url_res = urlopen(url).read()
print(url_res)
return inner
get_url()
总结:闭包好处,多次调用函数,不必多次生成外部函数的变量。
装饰器
装饰器的引出,计算程序的执行时间
import time
def func():
time.sleep(2)
def timer(f):
start = time.time()
f()
end = time.time()
print('函数运行的时间是:',end-start)
timer(func)
虽然上面的代码可以将实现将计算时间的函数分离,但是改变了原函数额调用方式
def func():
time.sleep(2)
def timer(f): # 装饰器函数
def inner():
start = time.time()
f() # 被装饰函数
end = time.time()
print('函数运行的时间是:',end-start)
return inner
func = timer(func)
func()
装饰器的作用:不想修改原来函数的调用方式,但是还想再原来的函数前后添加功能。
最终版没有改变函数的调用方式
def timer(f): # 装饰器函数
def inner():
start = time.time()
f() # 被装饰函数
end = time.time()
print('函数运行的时间是:',end-start)
return inner
@timer # 语法糖,@装饰器函数名
def func():
time.sleep(2)
func()
带返回值的装饰器
def timer(f): # 装饰器函数
def inner():
start = time.time()
res = f() # 被装饰函数
end = time.time()
print('函数运行的时间是:',end-start)
return res
return inner
@timer # 语法糖,@装饰器函数名
def func():
time.sleep(2)
return 'ok'
res = func()
print(res)
装饰带参数的函数装饰器
def timer(f): # 装饰器函数
def inner(*args,**kwargs):
start = time.time()
res = f(*args,**kwargs) # 被装饰函数
end = time.time()
print('函数运行的时间是:',end-start)
return res
return inner
@timer # 语法糖,@装饰器函数名
def func(a):
time.sleep(2)
print('参数:',a)
return 'ok'
res = func(1)
print(res)
装饰器的最终形式
def wrapper(func):
def inner(*args,**kwargs):
ret = func(*args,**kwargs)
return ret
return inner
@wrapper
def qqxing(): # qqing=wrapper(qqxing)
print(123)
qqxing()
print(qqxing.__name__) # inner
例子:通过使用装饰器来将要执行的函数的函数名写入文件中
import datetime
def log(func):
def inner(*args,**kwargs):
with open('log.txt','a',encoding='utf8') as f:
date_str = str(datetime.datetime.now())
f.write(date_str+func.__name__+'\n')
ret = func(*args,**kwargs)
return ret
return inner
@log
def qqxing():
print('qqxing')
qqxing()
通过别人写的装饰器模块,来得到被装饰函数的函数名
import datetime
from functools import wraps
def log(func):
@wraps(func)
def inner(*args,**kwargs):
with open('log.txt','a',encoding='utf8') as f:
date_str = str(datetime.datetime.now())
f.write(date_str+func.__name__+'\n')
ret = func(*args,**kwargs)
return ret
return inner
@log
def qqxing():
print('qqxing')
qqxing()
print(qqxing.__name__) # 如果没有加@wraps(func),qqxing__name__是inner,@wraps也成为装饰器修复技术
装饰器进阶
1、三层装饰器
import time
Flag = True
def timer_out(flag):
def timer(func):
def inner(*args,**kwargs):
if flag:
start = time.time()
ret = func(*args,**kwargs)
end = time.time()
print(end-start)
return ret
else:
ret = func(*args, **kwargs)
return ret
return inner
return timer
# timer = timer_out(Flag)
@timer_out(Flag) # <==>@timer
def wahaha():
time.sleep(2)
print('wahahah')
wahaha()
2、@wrapper2
@wrapper1
如上所示的装饰器
def warpper1(func):
def inner1(*args,**kwargs):
print("wrapper1,before func")
func()
print("wrapper1,after func")
return inner1
def warpper2(func):
def inner2(*args,**kwargs):
print("wrapper2,before func")
func()
print("wrapper2,after func")
return inner2
@warpper2 # foo=wrapper(foo)-->wrapper(inner1)==inner2
@warpper1 # 先执行这个装饰器 foo=warpper1(foo)==inner1
def foo():
print('foo')
foo()
#结果
# wrapper2,before func
# wrapper1,before func
# foo
# wrapper1,after func
# wrapper2,after func
3、面试题:免登录版装饰器
Flag = False
def login(func):
def inner(*args,**kwargs):
global Flag
if Flag:
ret = func()
return ret
else:
username = input('username:')
password = input('password:')
if username=='jack' and password=='123':
Flag=True
ret = func(*args,**kwargs)
return ret
else:
print('登录失败')
return inner
4、装饰器版的登录校验
这个例子其中的一些方法会在Django框架中学习到,这里只是为了总结装饰器而已。
def check_login(func):
def inner(requset,*args,**kwargs):
ret = requset.get_signed_cookie("is_login",default="0",salt="salt") # 加盐的cookie
if ret == '1':
# 已经登录过的继续执行
return func(requset,*args,**kwargs)
else:
next_url = requset.path_info # 获取当前访问的URL
return redirect("login/?next={}".format(next_url))
return inner
生成器
如下两种形式都是生成器
1、生成器函数:只要含有yield关键字的函数都是生成器,类似return,只能写在函数内部,不能和return共用。而且yield语句一次返回一个结果,在每个结果中间挂起原来的状态,以便下次从它开始离开的地方继续执行。
首先看普通函数的返回值
def generator():
print('============')
return '返回值函数'
g = generator()
print(g)
再看含有yield 的生成器函数
def generator():
print('>>>>>>>>>>')
yield '生成器函数'
g = generator()
print(g.__next__())
print(next(g))
# 生成器函数
# Traceback (most recent call last):
# File "D:/pythonstudy/装饰器迭代器生成器/生成器.py", line 16, in <module>
# print(next(g))
# StopIteration 如果取不到了值会报错为StopIteration
def generator():
print(1)
yield 'a'
print(2)
yield 'b'
g = generator()
print(g.__next__())
print(next(g))
生成器进阶
def generator():
print(123)
content = yield 1
print('content:', content)
print(456)
yield 2
g = generator()
print(g.__next__())
print(g.send('hello')) # send()和next()的效果一样
send获取下一个值的效果和next基本一样,只是在获取下一个值的时候,给上一个值的位置传递一个数据。
注意:第一次使用生成器的时候必须用next获取第一个值。
最后一个yield不能接受外部的值。
生成器面试题
def demo():
for i in range(4):
yield i
g = demo()
g1 = (i for i in g)
g2 = (i for i in g1)
print(list(g1)) #[0, 1, 2, 3]
print(list(g2)) #[]
注释:g1从g中取值直到取完,g2从g1中取到g1末,但是g2开始取值的时候,g1已经为空,所以该结果为空。
2、生成器表达式:本质迭代器【自带__iter__方法和__next__方法】
列表解析式 egg_list = ['鸡蛋%s'%i for i in range(10)]
生成器表达式 laomuji=('鸡蛋%s'%i for i in range(10))
迭代器
1、什么叫迭代
字符串、列表、元祖、字典、集合都可以被for循环,也可以通过Iterable来进行证明,这些都是可迭代的。
from collections import Iterable
l = [11,22,33]
t = (11,22,33)
d = {"name":"jack"}
s = {11,22,33}
print(isinstance(l,Iterable)) # True
print(isinstance(t,Iterable)) #True
print(isinstance(d,Iterable)) #True
print(isinstance(s,Iterable)) #True
2、可迭代协议
可迭代协议定义非常简单,就是内部实现了__iter__方法。
迭代器遵循迭代器协议:必须使用__iter__方法和__next__方法
3、类中的__iter__和__next__方法
class Foo:
def __init__(self,n):
self.n = n
def __iter__(self): # 成为一个可迭代对象。
return self
def __next__(self):
self.n += 1
return self.n
f = Foo(10)
f.__next__()
f.__next__()
for i in f:
if i>20:
break
print(i)
小练习:迭代器实现斐波那契数列
class Fib:
def __init__(self,a,b):
self._a = a
self._b = b
def __iter__(self):
return self
def __next__(self):
self._a,self._b = self._b,self._a + self._b
return self._a
f = Fib(1,1)
print(f.__next__()) # 1
print(f.__next__()) # 2
print(f.__next__()) # 3
print(f.__next__()) # 5
print(f.__next__()) # 8
print(f.__next__()) # 13
迭代器的好处:
1、从容器中一个一个的取值,会把所有的值都取到
2、节省内存空间,迭代器并不会在内存中再占用一大块内存,而是随着循环每次生成一个,每次next每次回给出一个值。
浙公网安备 33010602011771号