这个是一个难点,以后面试会经常出现的,要搞懂!
装饰器升级版,进阶内容1:
def outer(flag): def wrapper(func): def inner(*args,**kwargs): if flag: print('执行%s之前我加了一个功能'%func.__name__) ret = func(*args,**kwargs) #qqxing return ret return inner return wrapper @outer(True) #qqxing = wrapper(qqxing) def qqxing(): print('qqxing') @outer(False) #wahaha = wrapper(wahaha) def wahaha(): print('wahaha') qqxing() wahaha() #2000 #1000 #1000 # def outer(flag,flag2): # def wrapper(func): # def inner(*args,**kwargs): # ret = func(*args,**kwargs) # return ret # return inner # return wrapper # # @outer(True,False) #带参数的装饰器 ==》@wrapper ==》func = wrapper(func) ==> func == inner # def func():pass
上面是原代码,比较简洁。如下我又重新复制了一遍,以便做更加详细的注释。
注释开始:
def outer(flag):#这里需要加这个普通函数(没错这里的outer(flag)就是一个普普通通的函数而已,
#只不过它加在了装饰器的上面所以看起来好像很高级的样子)的目的是为了给装饰器里面的内容传一个参数,之所以传这个参数是为了调用里面加设的条件,如下所例:
def wrapper(func):
def inner(*args,**kwargs):
if flag:#接上面outer(flag)如果上面传入的flag是True那么就执行这里面的一步,否则就不执行。
#所以需要把上面的参数传入进来才可判断是否需要执行它
print('执行%s之前我加了一个功能'%func.__name__)#所以最后的最后,这里面需要加入什么样的判断条件或者什么功能都是可以随意改变的,
#完全取决于你想要什么样的需求
ret = func(*args,**kwargs) #qqxing
return ret
return inner
return wrapper#下面解释那么多都是为了给这里这一步做铺垫的,因为把True这个参数传进来了,函数调用后需要有返回值,
#这样后续才能有机会进入里面来接着执行没有执行完的内容。所以这里我们把wrapper给返回了。为了后续程序的执行做了铺垫。
@outer(True) #qqxing = wrapper(qqxing) 这里需要解释的是,既然上面加了一个函数outer(flag)所以这里需要调用它然后才可以进入该函数里面
#只有进入了函数里面才有机会执行函数内部的内容这里我们有专业术语叫做--函数体。所以把原本的@wrapper改成了最外层的函数的名字---@outer(True)
#同时传入了一个参数(True)作为判断条件给outer(flag)。然后,仔细看这一句话@ outer (True)这里面每一个都含藏玄机的,我来一一解释一下,
#‘@’这个符号是语法糖的符号,它后面接的是函数名这里是outer,仅仅这两个加在一起就足以调用一个函数了,但是函数名outer后面又加了一个括号(),
#形成了outer()这本身就足够条件调用一个函数了,然后括号里面还有一个True这就是一个函数在被调用的时候给它加入了一个参数,所以,你看,我们现在要如何判断呢
#是先判断@outer呢?还是先判断 outer(True)呢?现在来揭晓答案,是先判断outer(True),这一点是毫无疑问的,所以把outer(True)先调用,
#这里就是调用了上面最外层的函数outer(flag),同时把True这个参数传给了它。
#当把True传给了outer之后该函数的结果返回了,所返回的结果就是return wrapper,然后返回的结果被语法糖这里接收了。上面我们刚刚解释过了,
#@outer(True)这一句话的意思,根据逻辑可得,outer(True)的返回结果是wrapper,被语法糖接收,所以这里我们就把outer(True)替换成wrapper,
#那么得到的最终结果就是@wrapper。所以你看,我们绕了这么大一个圈最终回到了原点,是不是很奇妙啊,这就是语法的魅力,扯淡了。。。。
#当我们得到了最终的@wrapper这个结果的时候,那么我们在原本的简单的装饰器上面加的那一层outer函数就已经完成了它作为一普通函数的所有功能,
#然后它就消失在了代码的长河中,然后我们就可以把outer函数的内容给剥掉,最后得到的就是一个普通的装饰器的样子,然后你就只需要解决掉一个简单的装饰器即可了。
def qqxing():
print('qqxing')
@outer(False) #wahaha = wrapper(wahaha)这里跟上面是一样的,就是多加一个outer判断而已,按照上面的程序再接着走一遍,把outer(False)
#先传给outer函数然后把结果返回给语法糖。
#需要提醒的是,最外层的这个outer可以自定义改成任何名字,参数也可以随意变换,但是要遵循一条基本原则,
#那就是你所传的参数和你所接收的参数需要数量上保持一致
def wahaha():
print('wahaha')
qqxing()
wahaha()
#2000
#1000
#1000
# def outer(flag,flag2):
# def wrapper(func):
# def inner(*args,**kwargs):
# ret = func(*args,**kwargs)
# return ret
# return inner
# return wrapper
#
# @outer(True,False) #带参数的装饰器 ==》@wrapper ==》func = wrapper(func) ==> func == inner
# def func():pass
以上就是一个带参数的装饰器的所有内容了。
装饰器升级版,进阶内容2:
def wrapper2(func): #inner1 def inner2(*args,**kwargs): print('in wrapper 2,before') ret = func(*args,**kwargs) #inner1 print('in wrapper 2,after') return ret return inner2 def wrapper1(func): #qqxing def inner1(*args,**kwargs): print('in wrapper 1,before') ret = func(*args,**kwargs) #qqxing print('in wrapper 1,after') return ret return inner1 @wrapper1 #qqxing = inner1 @wrapper2 #qqxing = wrapper2(inner1) = inner2 def qqxing(): print('qqxing') qqxing() #inner2 #login 一个实现login的装饰器 #timmer 一个计算函数时间的装饰器 #先登录,再计算时间
同上,为了更方便理解,我把它复制过来以便于加注释:
执行步骤已经标注了。
1# def wrapper2(func): #qqxing #根据下面wrapper2上传的参数得到的结果,此时qqxing就等于func,
5# def inner2(*args,**kwargs): #这里是inner2函数,被调用了
10# print('in wrapper 2,before') #inner2被调用后打印这里
11# ret = func(*args,**kwargs) #qqxing #根据闭包函数的理论,这里的func是调用的上一层函数的参数,所以这里的func也等于qqxing
#按照从上到下原则,打印完之后到了这一步,这里又遇到了等式,然后先计算右边的,此时的func已经等于qqxing,所以这里调用了qqxing函数。
15# print('in wrapper 2,after') #上面走完了就到这里继续接着走下去,打印这一步。
16# return ret #然后顺着下来,返回结果
6# return inner2 #这里把wrapper2函数的结果返回了,
2# def wrapper1(func): #inner2 根据传参数的结果这里的func就等于inner2,
7# def inner1(*args,**kwargs): #这里的inner1被调用了
12# print('in wrapper 1,before') #打印结果
13# ret = func(*args,**kwargs) #inner2 同理根据闭包理论这里func调用的是上一层函数,所以这里的func也是inner2
#上一行被打印完了之后,到这里的时候是一个等式先计算等式右边的,右边的func已经等于了inner2了,所以在这一步调用了inner2函数
17# print('in wrapper 1,after') #按照顺序下来就轮到了这里,打印结果
18# return ret #这里就是最后一步
8# return inner1 #这里把wrapper1的函数结果返回了
先定义函数这一点没什么可解释的,这里是语法糖,在定义过函数后就走到了这里,然后走下面的语法糖
3# @wrapper1 #qqxing = inner1 #这里需要着重解释一下原因,然后是这里,@wrapper1----就解释为qqxing=wrapper1(inner2)之所以是这样的是因为,
#下面的wrapper2已经被调用执行过了,所以是qqxing=,然后=后面的内容应该是wrapper1(qqxing),但是下面的wrapper2执行之后把qqxing改变成了inner2了,所以括号里面应该是inner2
#所以最终得到的就是qqxing=wrapper1(inner2),把wrapper1调用了,然后把参数inner2传参数给wrapper1函数。上面调用完了该函数后得到的返回值是inner1.所以最终qqxing=inner1.
4# @wrapper2 #qqxing = wrapper2(inner1) = inner2 #这里得到的是跟之前学过的一样的结果,@wrapper2就解释为----qqxing=wrapper2(qqxing)
#然后开始调用wrapper函数顺便把qqxing作为参数传给wrapper2函数 。根据上面的执行结果得到的返回值inner2,所以上面的qqxing=wrapper2(qqxing)最终的返回结果就是inner2.
#所以最终的结果就是qqxing=inner2(qqxing的物理地址是inner2,表现形式是qqxing。这一句是老师的原话,如果不理解的话可以忽略不计的。)
def qqxing():
14# print('qqxing')#函数被调用然后打印出来
9# qqxing() #inner1 然后根据程序从上到下执行的结果,走到了这一步,这里的qqxing现在是inner1。所以qqxing()就等于inner1()。这里调用了inner1函数
根据上面的inner2里面的执行结果到了这一步,然后调用这里的qqxing函数
唯一难懂的一点就是语法糖的部分,一叠加之后,就会容易混淆。
思维导图:
1 import time 1 2 def time(func): 2 3 def inner(*args,**kwargs): 4 4 start=time.time() 8 5 re=func(*args,**kwargs) 9 6 print(time.time-start) 12 7 return re 13 8 return inner 5 9 @time 3 10 def func2(): 6 11 print('shero') 10 12 print('so what') 11 13 14 func2() 7 15 16 #程序内部的执行顺序
前情回顾:
1 # a=10 2 # b=20 3 # def tes5(a,b): 4 # print(a,b) 5 # c = tes5(b,a) 6 # print(c) 7 8 # a=10 9 # b=20 10 # def tes5(a,b): #a = 20,b = 10 11 # a=3 12 # b=5 13 # print(a,b) 14 # c = tes5(20,10) 15 # print(c) 16 17 # x = 1 18 # def f(x): 19 # print(x) 20 # 21 # ret = f(10) 22 # print(ret) 23 # print(x) 24 25 def f1(): 26 a = 1 27 def f2(): 28 def f3(): 29 print(a) 30 f3() 31 print('f3 : ',f3.__closure__) 32 f2() 33 print('f2 : ',f2.__closure__) 34 35 f1() 36 37 #动态参数*args,**kwargs :位置参数,*args,默认参数,**kwargs 38 #命名空间和作用域 39 #作用域链 40 #函数名是第一类对象的概念
闭包:(为了后面的装饰器做铺垫的)
1 # def f1(b): 2 # def f2(): 3 # print(b) 4 # f2() 5 # 6 # f1() 7 #闭包的定义 8 #内部的函数引用了外部函数的变量 9 10 # def f1(b): #闭包的常用状态 11 # def f2(): 12 # print(b) 13 # return f2 14 # 15 # 16 # # f2 = f1('bbb') 17 # ff = f1('bbb') 18 # ff() #==f1('bbb')() 19 20 # def f1(): #从内部函数返回一个值到全局 21 # b = 10 22 # def f2(): 23 # return b 24 # return f2() 25 # 26 # print(f1()) 27 28 from urllib.request import urlopen 29 # ret = urlopen('http://www.cnblogs.com/Eva-J/articles/7194277.html').read() 30 # print(ret) 31 def get_url(url): 32 def read1(): 33 ret = urlopen(url).read() 34 print(ret) 35 return read1 36 37 read_func = get_url('http://www.cnblogs.com/Eva-J/articles/7194277.html') 38 read_func() 39 read_func()
装饰器:
1 #讲故事 2 #带着你一步一步的剖析装饰器的成因 3 #关于时间: 4 import time #模块 5 # start_time = time.time() 6 # time.sleep(1) 7 # end_time = time.time() 8 # print('=====%s====='%(end_time-start_time)) 9 def timmer(func): 10 def inner(): 11 start_time = time.time() 12 time.sleep(0.1) 13 func() 14 end_time = time.time() 15 print('=====%s=====' % (end_time - start_time)) 16 return inner 17 18 def func(): 19 print('公司好老板好同事好') 20 21 # func = timmer(func) 22 # func() 23 24 #装饰器的作用 25 # 在不改变函数的调用方式的情况下,给函数的前后添加新的功能 26 27 #从最简单的装饰器 28 def timmer(qqxing): #timmer是装饰器的名字,传入的参数就是被装饰的函数 29 def inner(): #在装饰器中需要定义一个内部函数 30 print('调用func之前') 31 qqxing() #被装饰的函数,并且要执行 32 print('调用func之后') 33 return inner #将内部函数的名字返回 34 35 @timmer #语法糖 func = timmer(func) 36 def func(): 37 print('公司好老板好同事好') 38 39 # func() 40 # 完整的装饰-万能的装饰 41 def timmer(qqxing): #timmer是装饰器的名字,传入的参数就是被装饰的函数 42 def inner(*args,**kwargs): #在装饰器中需要定义一个内部函数 43 print('调用func之前') 44 ret = qqxing(*args,**kwargs) #被装饰的函数,并且要执行 45 print('调用func之后') 46 return ret 47 return inner #将内部函数的名字返回 48 49 @timmer #语法糖 func = timmer(func) 50 def func(name): 51 print('%s的公司好老板好同事好'%(name)) 52 return 1111111111 53 54 ret = func('俊杰') 55 print('result : %s'%ret) 56 57 #装饰器的固定结构 58 def wrapper(func): 59 def inner(*args,**kwargs): 60 """被装饰函数执行之前要添加的代码""" 61 ret = func(*args,**kwargs) 62 """被装饰函数执行之后要添加的代码""" 63 return ret 64 return inner 65 66 '''def timmer(qqxing): #第一步定义一个函数 #第六步接收了timmer传上来的参数func。此时qqxing=func #此时timmer=inner是被return返回的值 67 def inner(): #第四步,定义一个函数 68 qqxing() #第九步因为inner被调用所以执行inner里面的内容,因为第六步的时候qqxing已经被func赋值了,所以这一步就是func()等于调用了func()函数 69 return inner #第五步因为没有调用该函数,所以直接走下面不走函数里面。此时把inner返回给了return所在的函数timmer里,return在timmer里缩进的,所以return的位置决定了它是timmer的返回值 70 def func(): #第二步定义一个函数 71 print('hello') ##第十步因为func()函数被执行了,所以这一步就被打印出来了。 72 func=timmer(func) #第三步先计算=右边的 调用了timmer函数同时还传了一个参数 #第七步timmer的函数被return返回为inner,下面的timmer也是跟上面的timmer一样的所以要返回来,然后赋值给左边的func 73 func() #第八步因为没有调用函数,所以执行下一步func()因为上一步的func被赋值为inner,所以这一步的func调用就是等于inner()调用 74 '''
课堂小练习:
里面有一个是面试题,log。
1 #概念 2 #开放封闭原则 3 #开放 4 #对扩展是开放的 5 6 #封闭 7 #对修改是封闭的 8 9 #装饰器:开放封闭原则 10 ''' 11 flag = False 12 13 def login(func): 14 def inner(*args,**kwargs): 15 global flag 16 if flag == False: 17 username = input('用户名:') 18 password = input('密码:') 19 if username == 'alex' and password == 'somebody': 20 print('登录成功') 21 flag = True 22 if flag == True: 23 ret = func(*args,**kwargs) 24 return ret 25 return inner 26 27 @login 28 def art(): 29 print('欢迎来到文章页') 30 31 @login 32 def dar(): 33 print('欢迎来到日记页') 34 35 #用装饰器实现,访问art或者dar函数,登陆一次之后,无需再次登录 36 art() 37 dar() 38 ''' 39 def log(func): 40 def inner(*args,**kwargs): 41 print('你要调用%s函数了'%func.__name__) 42 ret = func(*args,**kwargs) 43 return ret 44 return inner 45 46 @log 47 def f1(): 48 print('f1') 49 @log 50 def f2(): 51 print('f2') 52 53 #日志 log 必须会! 54 f1() 55 f2() 56 57 #背诵装饰器的固定结构 58 #复习函数以及装饰器的所有内容、继续完成思维导图和博客 59 #上面的登录、日志的题目 搞懂 60 #预习: 61 # 装饰器的进阶用法 62 # 迭代器和生成器
后期会补上思维导图,,,