Day 04 :装饰器初识和闭包简介
1 函数的进阶
python不是内存级别的编程语言,C语言是内存级别的编程语言,有指针的概念。
我们首先回忆一下Python代码运行的时候遇到函数是怎么做的,从Python解释器开始执行之后,就在内存中开辟里一个空间,每当遇到一个变量的时候,就把变量名和值之间对应的关系记录下来,但是当遇到函数定义的时候,解释器只是象征性的将函数名读如内存,表示知道这个函数存在了,至于函数内部的变量和逻辑,解释器根本不关心。
等执行到函数调用的时候,Python解释器会再开辟一块内存来储存这个函数里面的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量回储存在新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。
我们给这个‘存放名字与值的关系’的空间起了一个名字-------命名空间。
代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间;
在函数的运行中开辟的临时的空间叫做局部命名空间。
名称空间:包括全局名称空间,局部名称空间,内置名称空间。
全局名称空间:程序运行时,存放变量与值的关系
临时名称空间:也叫局部名称空间。存入函数里面的变量与值的关系,随着函数的执行结束,临时名称空间消失
内置名称空间:创建py文件后,就会把内置名称空间加载到内存
作用域:全局作用域:包括全局名称空间 、内置名称空间
局部作用域:局部名称空间
加载顺序:内置名称空间--->全局名称空间--->局部名称空间(函数执行时)
取值顺序:局部名称空间--->全局名称空间--->内置名称空间 (单向不可逆)
2 内置函数:globals() 和 local() :查看名称空间里的值
基于字典的形式获取局部变量和全局变量
globals()——获取全局变量的字典
locals()——获取执行本方法所在命名空间内的局部变量的字典
# globals() 把全局变量和值的关系放在字典中 # locals() 把局部变量与值的关系放在字典中 name1='Lucy' def func1(): name2 = 'Lily' print(globals()) print(locals()) func1()
3 关键字
3.1 global 引用并改变一个全局变量 在局部作用域声明一个全局变量
# 引用并改变一个全局变量 count = 1 def func1(): global count # 引用并改变一个全局变量 count = count + 1 print(count) # 2 func1() # 在局部作用域声明一个全局变量 def func1(): global name # 声明一个全局变量 name = 'xiaobai' print(name) # xiaobai func1() print(name) # xiaobai # 打印global声明的全局变量name
3.2 nonlocal 不能操作全局变量 可以对局部作用域变量进行修改和引用,从哪层引用的局部变量,就从那层全部改变
def func1(): count = 1 def inner(): nonlocal count # 引用局部变量 count = count + 1 print(count) # 2 inner() print(count) # 2 # 从哪层引用的局部变量,就从那层改变,count从1变为2 func1()
3.3 取值
1. 引用而不是改变
2. 取值是从小到大取值 :LEGB
3. 想改变上层空间的变量,要用到global 或nonlocal
int、str 、bool 不可变数据类型需要nonlocal改变 list、dict、set(三种可变数据类型)不需要nonlocal改变 list = [555] def func1(): list.append(666) print(list) # 555,666 func1() # 如果默认参数是一个可变的数据类型,那么他在内存中永远是一个 def exentdList(val,list = []): list.append(val) return list list1 = exentdList(10) list2 = exentdList(123,[]) list3 = exentdList('a') print('list1 = %s'%list1) # [10,'a'] print('list2 = %s'%list2) # [123] print('list3 = %s'%list3) # [10,'a']
4 函数名的应用
像这样的函数名,有一个叫法,叫“第一类对象”
1. 打印函数名
2. 函数名可以作为容器类数据的元素
3. 函数名可以作为函数的参数
4. 函数名可以作为函数的返回值
# 打印函数名 def func1(): print(666) print(func1) # <function func1 at 0x0000000001D11E18> # 函数名可以作为容器类数据的元素 def func1(): print(111) def func2(): print(222) def func3(): print(333) l1 = [func1, func2, func3] for i in l1: i() # 111 222 333 # 函数名可以作为函数的参数 def func1(): print(111) def func2(x): x() # x为func1,加()执行func1 print(222) func2(func1) # 把func1 传给x # 先执行func1,打印结果111 222 # 函数名可以作为函数的返回值 def func1(): print(111) def func2(x): #1. x = func1 print(222) #2. 打印222 return x #3. 返回x(func1)给调用者func2(func1) func2(func1)() #4. func1 加() 执行函数
5 闭包
闭包定义:内层函数对外层函数非全局变量的引用,叫做闭包
闭包机制:如果python解释器遇到闭包,这个闭包不会随着函数的结束而被释放掉
闭包应用:装饰器、爬虫
判断闭包的方法: __closuer__ 是闭包返回cell,不是闭包返回None
#闭包常用方法 def wrapper(): name = 'xiaobai' def inner(): print(name) # xiaobai # 内层函数对外层函数非全局变量的引用 inner() wrapper() # 输出的__closure__有cell元素 :是闭包函数 def wrapper(): name = 'xiaobai' def inner(): print(name) # xiaobai inner() print(inner.__closure__) # 闭包返回cell (<cell at 0x0000000001E394C8: str object at 0x00000000022380D8>,) wrapper() # 输出的__closure__为None :不是闭包函数 name = 'xiaobai' def wrapper(): def inner(): print(name) # xiaobai # 内层函数对外层函数非全局变量的引用 inner() print(inner.__closure__) # None 非闭包返回None wrapper() # 通过参数传值也属于闭包 def wrapper(argv):#通过参数传也是闭包 def inner(): print(argv) inner() print(inner.__closure__) wrapper('xiaobai')
# 爬虫中使用到的闭包 # 如果爬取得数据量比较大时,如果没有闭包机制,每次爬取得内容都会占用内存,但是有闭包的机制,第一次爬取时已经读到内存,以后爬取时都是从内存的读到的数据,并不是从url在爬取一次 # 装饰器,装饰器的基本原理就是通过闭包实现的不用担心爬取的内容会一直占用内存,python内部有垃圾回收机制,如果内存在一定时间没有用,会自动在内存中关闭 # 垃圾回收机制,有一个类似于计数器,记录所有的变量,超过固定的时间没有用,在内存中清除 def index(): url = "http://www.xiaohua100.cn/index.html" def get(): return urlopen(url).read()#urlopen(url).read()意思是将url所有内容读取出来 return get xiaohua = index() content1 = xiaohua() content2 = xiaohua() #第二次执行函数时,是从内存中读取的数据,不会 重新将url的内容重新在读一遍 print(content1.decode())
6 装饰器
import time print(time.time()) # 时间戳,显示当前时间1527734577.7906015 # 计算一个函数的执行效率 import time def func1(): time.sleep(0.5) print('非常复杂...') start_time = time.time() func1() end_time = time.time() print('此函数的执行效率%s'%(end_time - start_time)) # 改版1:封装到一个函数中 import time def func1(): time.sleep(0.5) print('非常复杂...') def func2(): time.sleep(0.5) print('特别复杂...') start_time = time.time() func1() end_time = time.time() print('此函数的执行效率%s'%(end_time - start_time)) start_time = time.time() func2() end_time = time.time() print('此函数的执行效率%s'%(end_time - start_time)) # 改版2:被测试函数当参数传入,可以测试多个函数的执行效率 import time def func1(): time.sleep(0.5) print('非常复杂...') def func2(): time.sleep(0.5) print('特别复杂...') def timmer(f): start_time = time.time() f() end_time =time.time() print('此函数的执行时间%s'%(end_time - start_time)) timmer(func1) timmer(func2) # 改版3:测试函数执行效率的同时不要改变原函数的调用方式 import time def func1(): time.sleep(0.5) print('非常复杂...') def func2(): time.sleep(0.5) print('特别复杂...') def timmer(f): start_time = time.time() f() end_time = time.time() print('此函数的执行时间%s' % (end_time - start_time)) f1 = func1 func1 = timmer func1(f1) # timmer(func1) # 改版4:改版3虽然大体上满足了我的要求,但是增加两行代码,而且多了参数,不好, 继续改:尽量不添加其他代码,而且做到调用时一模一样。 import time def func1(): time.sleep(0.3) print('非常复杂......') def func2(): time.sleep(0.3) print('特别复杂......') def timmer(f): # timmer(func1) def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return inner # 把inner 赋值 给timmer(func1# ) func1 = timmer(func1) # func1 = inner func2 = timmer(func2) # func2 = inner func1() # inner() func2() # inner() # 改版5:改版4每次测试一个函数的执行效率时,都需要加一行 func1 = timmer(func1)代码,麻烦 # python提出了一个语法糖@ import time def timer(f): # 2. 接受传参,timer(f) = timer(func1) def inner(): # 5. 执行inner函数 start_time = time.time() f() # 6. f = func1,执行func1()函数 end_time = time.time() print('此函数的执行效率%s' %(end_time - start_time)) return inner # 3. 把 inner 返回给 timer(func1),也就是 func1 = inner @timer # 1. func1 = timer(func1) def func1(): time.sleep(0.5) print('非常复杂...') func1() # 4. 此时func1是inner,执行inner函数 # 改版6:被装饰的函数肯定要有参数的,你现在不能满足,解决这个问题。 # 被装饰的函数带参数的装饰器 import time def timer(f): # 2. 把 func1 传给f, f = func1 timer(func1) # 5. 把 func2 传给f,f = func2 timer(func2) def inner(*args,**kwargs): start_time = time.time() f(*args,**kwargs) #8. founc2 (1,2,sex = nv, name = alex) end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return inner # 3. 返回给 timer(func1),也就是 func1 func1 = inner # 6. func2 = inner @timer # 1. func1 = timer(func1) def func1(a,b): time.sleep(0.5) print(a,b) print('非常复杂......') @timer # func2 = timer(func2) # 4. 把 func2 传参给f,func2 = timer(func2) def func2(a,b,name,sex='man'): time.sleep(0.5) print(a,b,sex,name) print('特别复杂......') func2(1,2,sex='nv',name='alex') #7. func2 = inner 执行inner函数 把(1,2,sex = 'nv' , name = 'alex') 传给 *args,**kwargs # 改版7:被装饰的函数肯定要有返回值的,解决这个问题。 # 被装饰的函数带参数且有返回值的装饰器 import time def timer(f): # 2. timer(f) = timer(func2) def inner(*args,**kwargs): start_time = time.time() ret = f(*args,**kwargs) end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return ret return inner #3. inner = timer(func2) @timer # func2 = timer(func2) #1. 把 func2 传给f,f = func2 def func2(a,b,name,sex='man'): time.sleep(0.3) print(a,b,sex,name) print('非常复杂......') return 666 print(func2(1,2,sex='nv',name='alex')) def timer(f): def inner(*args,**kwargs): start_time = time.time() ret = f(*args,**kwargs) end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return ret return inner @timer # func2 = timer(func2) def func2(a,b,name,sex='man'): time.sleep(0.3) print(a,b,sex,name) print('非常复杂......') return 666 ret1 = func2(1,2,sex='nv',name='alex') print(ret1) def wrapper(f): def inner(*args,**kwargs): """被装饰函数执行之前的操作""" ret = f(*args,**kwargs) """被装饰函数执行之后的操作""" return ret return inner
7 装饰器进阶
带参数的装饰器装饰一个函数 1,将@ 与函数分开@ timmerout(flag) 返回了timmer 2,将@timmer结合 def timmerout(flag1): # flag1 =flag def timmer(f): def inner(*args,**kwargs): if flag1: start_time = time.time() ret = f(*args,**kwargs) end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return ret else: ret = f(*args, **kwargs) return ret return inner return timmer flag = False @timmerout('alex','nan',1000) # 1,将@ 与函数分开@ timmerout(flag) 返回了timmer 2,将@timmer结合。 def func1(): time.sleep(0.3) print('非常复杂......') return 666 @timmerout(flag) def func2(): time.sleep(0.3) print('非常复杂......') return 666 @timmerout(flag) def func3(): time.sleep(0.1) print('非常复杂......') return 666 # 例子: def timmerout(flag1): # flag1 =flag def timmer(f): def inner(*args,**kwargs): if flag1: start_time = time.time() ret = f(*args,**kwargs) end_time = time.time() print('此函数的执行效率%s' % (end_time - start_time)) return ret else: ret = f(*args, **kwargs) return ret return inner return timmer @timmerout('京东') # 1,将@ 与函数分开@ timmerout(flag) 返回了timmer 2,将@timmer结合。 def JDshop(): time.sleep(0.3) print('非常复杂......') return 666 @timmerout('京东') def JD(): time.sleep(0.3) print('非常复杂......') return 666 @timmerout('淘宝') def taobao(): time.sleep(0.1) print('非常复杂......') return 666 @timmerout('淘宝') def taobaoshop(): time.sleep(0.1) print('非常复杂......') return 666 func1() func2() func3() 多个装饰器装饰一个函数 def wrapper1(func): # func = f函数名 def inner1(): print('wrapper1 ,before func') # 2 func() # f函数名() print('wrapper1 ,after func') # 4 return inner1 def wrapper2(func): # func = inner1 def inner2(): print('wrapper2 ,before func') # 1 func() # inner1() print('wrapper2 ,after func') # 5 return inner2 def wrapper3(func): def inner3(): print('wrapper3 ,before func') func() print('wrapper3 ,after func') return inner3 @wrapper3 @wrapper2 # f = warpper2(f) 里面的f是inner1 外面的f是inner2 @wrapper1 # f = warpper1(f) 里面的f函数名 外面的f 是inner1 def f(): print('in f') # 3 f() # inner2() # 开放封闭原则 对扩展开放 对修改原内容封闭