装饰器行远自迩,详解Python装饰器由表及里

By admin in 4858.com on 2019年4月15日

1、解释器入门

详解Python装饰器遵纪守法,详解python奉公守法

装饰器的功能在无数言语中都有,名字也不尽同样,其实它呈现的是一种设计形式,重申的是开放封闭原则,更加多的用来前期效益进步而不是编写制定新的代码。装饰器不光能装饰函数,也能装饰其余的对象,比如类,但常常,大家以装修函数为例子介绍其用法。要知道在Python中装饰器的原理,需求一步一步来。本文尽量描述得浅显易懂,从最基础的剧情讲起。

(注:以下使用Python三.五.1条件)

一、Python的函数相关基础

先是,必须重申的是python是从上往下依次试行的,而且蒙受函数的概念代码块是不会马上施行它的,只有等到该函数被调用时,才会实行其中间的代码块。

def foo():
print("foo函数被运行了!") 
如果就这么样,foo里的语句是不会被执行的。
程序只是简单的将定义代码块读入内存中。

再看看,顺序推行的事例:

def foo():
 print("我是上面的函数定义!")
def foo():
 print("我是下面的函数定义!")
foo()
运行结果:
我是下面的函数定义

足见,因为种种试行的原故,上面包车型地铁foo将上边的foo覆盖了。因而,在Python中代码的放置地点是有须求的,不能够随意摆布,函数体要放在被调用的言语在此之前。

 其次,我们还要先搞精通几样东西:函数名、函数体、重回值,函数的内部存款和储蓄器地址、函数名加括号、函数名被看成参数、函数名加括号被看作参数、再次回到函数名、重临函数名加括号。对于如下的函数:

 def foo():
 print("让我们干点啥!")
 return "ok"
 foo()  

     函数名:        foo

  函数体:        第1-3行

  重返值:        字符串“ok”    假使不显式给出return的靶子,那么默许再次回到None

  函数的内部存款和储蓄器地址:    当函数体被读进内存后的保留地点,它由标记符即函数名foo引用,
                                                  
也正是说foo指向的是函数体在内部存款和储蓄器内的保留地点。

  函数名加括号:      
例如foo(),函数的调用方法,只有见到那么些括号,程序会根据
                                                  
函数名从内部存款和储蓄器中找到函数体,然后实行它

再看上边那个例子:

def outer(func):
 def inner():
 print("我是内层函数!")
 return inner
def foo():
 print("我是原始函数!") 
outer(foo)
outer(foo())

在python中,一切都是对象,函数也不例外。由此能够将函数名,甚至函数名加括号举办调用的主意作为另2个函数的重返值。下面代码中,outer和foo是多少个函数,outer(foo)表示将foo函数的函数名当做参数字传送递给outer函数并奉行outer函数;outer(foo())表示将foo函数实施后的重返值当做参数字传送递给outer函数并实施outer函数,由于foo函数未有点名重返值,实际上它传递给了outer函数1个None。注意个中的差距,有没有括号是重点!

 同样,在outer函数内部,再次来到了二个inner,它是在outer函数内部定义的1个函数,注意,由于inner前边未有加括号,所以回来的是inner的函数体,实际上相当于inner那个名字,二个轻松的引用而已。那么,如若outer函数重临的是inner()呢?以往你应有已经很掌握了,它会先实行inner函数的内容,然后再次来到个None给outer,outer再把那么些None重返给调用它的靶子。

 请记住,函数名、函数加括号能够被作为参数字传送递,也得以被作为再次来到值return,有未有括号是多个精光不相同的意思!

2、装饰器的行使情状

   
装饰器日常用于在不改换原来函数代码和职能的景况下,为其增加额外的效能。比如在原函数实行前先进行点什么,在试行后实践点什么。

 让大家通过二个例子来看望,装饰器的应用景况和反映的设计形式。(抱歉的是自身陈设不出越来越好的情状,只好引用武大神的案例加以演绎)

 有三个大商厦,下属的基础平台部负责内部应用程序及API的开辟,有不计其数个业务部门负责不相同的业务,他们分别调用基础平台部提供的区别函数处理本身的事务,景况如下: 

# 基础平台部门开发了上百个函数
def f1():
 print("业务部门1数据接口......")
def f2():
 print("业务部门2数据接口......")
def f3():
 print("业务部门3数据接口......")
def f100():
 print("业务部门100数据接口......") 
#各部门分别调用
f1()
f2()
f3()
f100()

    
由于集团在创业初期,基础平台部开辟那个函数时,由于各个缘由,比如时间,比如驰念不周等等,没有为函数调用举办安全认证。今后,平台部COO决定弥补那些毛病,于是:

 
第二遍,COO叫来了二个运营工程师,工程师跑上跑下各种机构开始展览通报,让她们在代码里增加认证成效,但是,当天她被开掉了。

 第2遍:CEO又叫来了三个运行工程师,工程师用shell写了个复杂的剧本,勉强落成了成效。但他连忙就回去接着做运转了,不会付出的运行不是幸亏维….

 首次:主任叫来了3个python自动化开垦工程师,男生是那样干的:只对基础平台的代码进行重构,让N个业务部门无需做其余修改。那汉子一点也不慢也被开了,连续运输行也没得做。  

def f1():
 #加入认证程序代码
 print("业务部门1数据接口......")
def f2():
 # 加入认证程序代码
 print("业务部门2数据接口......")
def f3():
 # 加入认证程序代码
 print("业务部门3数据接口......")
def f100():
 #加入认证程序代码
 print("业务部门100数据接口......")
#各部门分别调用
f1()
f2()
f3()
f100()

 第陆回:首席实施官又换了个
工程师,他是这么干的:定义个表明函数,原来其余的函数调用它,代码如下框。不过,老总仍然不惬意,不过那壹次他表明了为何。高管说:写代码要依据开放封闭原则,即便在那些规则首要是本着面向对象开荒,然而也适用于函数式编制程序,简单的讲,它规定已经落到实处的职能代码内部不允许被修改,但外表能够被扩展,即:封闭:已兑现的作用代码块;开放:对扩张开放。假若将绽放封闭原则应用在上述须求中,那么就不容许在函数
f一、f二、f三……f⑩0的中间开始展览代码修改。遗憾的是,工程师未有特出的女对象,所以高速也被开除了。

def login():
 print("认证成功!")
def f1():
 login()
 print("业务部门1数据接口......")
def f2():
 login()
 print("业务部门2数据接口......")
def f3():
 login()
 print("业务部门3数据接口......")
def f100():
 login()
 print("业务部门100数据接口......")
#各部门分别调用
f1()
f2()
f3()
f100()

   
第肆回:已经未有时间让主持找别人来干那活了,他调整亲自上阵,并且打算在函数试行后再追加个日志功用。经理是这么想的:不会装饰器的掌管不是好码农!要不为什么笔者能当主持,你不得不被管呢?嘿嘿。他的代码如下:

#/usr/bin/env python
#coding:utf-8
def outer(func):
 def inner():
 print("认证成功!")
 result = func()
 print("日志添加成功")
 return result
 return inner
@outer
def f1():
 print("业务部门1数据接口......")
@outer
def f2():
 print("业务部门2数据接口......")
@outer
def f3():
 print("业务部门3数据接口......")
@outer
def f100():
 print("业务部门100数据接口......")
#各部门分别调用
f1()
f2()
f3()
f100()

对此上述代码,也是仅需对基础平台的代码进行实行,就能够完毕在其余单位调用函数
f一 f二 f三 f100
在此以前都实行求证操作,在操作截止后保存日志,并且别的业务部门无需他们自身的代码做任何修改,调用方式也不用变。“老董”写完代码后,觉得独乐了不比众乐乐,打算装逼一下,于是写了篇博客将经过进展了详实的认证。

 3、装饰器的内部原理、

 上边大家以f1函数为例实行求证:

 def outer(func):
 def inner():
 print("认证成功!")
 result = func()
 print("日志添加成功")
 return result
 return inner
@outer
def f1():
 print("业务部门1数据接口......")

 运用大家在第3局地介绍的学问来分析一下方面那段代码:

  • 程序初始运维,从上往下编写翻译,读到def
    outer(func):的时候,发现那是个“一等平民”->函数,于是把函数体加载到内部存款和储蓄器里,然后过。
  • 读到@outer的时候,程序被@那几个语法糖吸引住了,知道那是个装饰器,按规矩要立时施行的,于是程序开端运营@后边那么些名字outer所定义的函数。(相信未有人会愚昧的将@outer写到其余地点,它不得不放在被点缀的函数的顶端近年来处,不要空行。)
  • 程序再次来到到outer函数,起初要推荐行李装运饰器的语法规则,那有个别规则是定死的,是python的“法律”,不要问何故。规则是:被点缀的函数的名字会被看成参数字传送递给装修函数。装饰函数试行它和谐内部的代码后,会将它的再次来到值赋值给被点缀的函数。 

一般来讲图所示:

4858.com 1

那当中必要小心的是:

  • @outer和@outer()有分别,未有括号时,outer函数照旧会被实施,那和历史观的用括号技艺调用函数分歧,必要尤其注意!那么有括号呢?那是装饰器的高档级用法了,未来会介绍。
  • 是f一以此函数名(而不是f壹()那样被调用后)当做参数字传送递给装饰函数outer,也正是:func

    f1,@outer等于outer(f一),实际上传递了f壹的函数体,而不是实践f1后的重回值。

  • outer函数return的是inner那么些函数名,而不是inner()那样被调用后的重返值。

只要您对第三部分函数的基础知识有显然的打听,那么地点的内容你应当很轻易通晓。

 四.
程序伊始实施outer函数内部的故事情节,一同始它又遇上了一个函数,很绕是啊?当然,你能够在
inner函数前后安顿点其余代码,但它们不是重大,而且有点小麻烦,下边会解释。inner函数概念块被先后阅览到后不会及时实施,而是读入内部存款和储蓄器中(那是潜规则)。

 5. 再往下,碰到return

1、必备

#### 第一波 ####

def foo():

``print 'foo'

 

foo     ``#表示是函数

foo()   ``#表示执行foo函数

 

#### 第二波 ####

def foo():

``print 'foo'

 

foo ``= lambda x: x ``+ 1

 

foo()   ``# 执行下面的lambda表达式,而不再是原来的foo函数,因为函数 foo 被重新定义了

 

写代码要遵循开放封闭原则,那么怎么着是开放封闭原则吗,一言以蔽之正是:已经落到实处的意义代码块不一致意被涂改,但能够被扩充。即:

inner,重返值是个函数名,并且这几个函数名会被赋值给f一以此被点缀的函数,也正是f一

inner。依照前边的文化,大家清楚,此时f一函数被新的函数inner覆盖了(实际上是f一这一个函数名改成成指向inner那一个函数名指向的函数体内部存款和储蓄器地址,f1不再指向它原本的函数体的内部存款和储蓄器地址),再现在调用f一的时候将实践inner函数内的代码,而不是原先的函数体。那么先前的函数体去哪了?还记得大家将f一看成参数字传送递给func这一个形参么?func这一个变量保存了老的函数在内部存储器中的地址,通过它就足以进行老的函数体,你能在inner函数里观望result =
func()这句代码,它就是这么干的!

 6.接下来,还平昔不截止。当业务部门,依旧通过f一()的艺术调用f壹函数时,推行的就不再是老的f1函数的代码,而是inner函数的代码。在本例中,它首先会打印个“认证成功”的晋升,很扎眼你能够换来自由的代码,那只是个示范;然后,它会奉行func函数并将重返值赋值个变量result,那个func函数正是老的f一函数;接着,它又打字与印刷了“日志保存”的提示,这也只是个示范,能够换来任何你想要的;最后回来result那几个变量。大家在业务部门的代码上得以用
r = f1()的措施接受result的值。

 七.以上流程走完后,你应有看出来了,在并没有对业务部门的代码和接口调用情势做其它改造的还要,也绝非对基础平台部原有的代码做内部修改,仅仅是增多了三个装饰函数,就兑现了我们的必要,在函数调用前先验证,调用后写入日志。那正是装饰器的最大效果。

 难题:那么为何大家要搞四个outer函数三个inner函数这么复杂呢?1层函数不行呢?

 答:请小心,@outer那句代码在程序实施到此地的时候就会自行实行outer函数内部的代码,借使不封装一下,在业务部门还未进行调用的时候,就举办了些什么,那和初衷有点不符。当然,假若你对这么些有须求也不是可怜。请看上边的事例,它唯有一层函数。

def outer(func):
 print("认证成功!")
 result = func()
 print("日志添加成功")
 return result
@outer
def f1():
 print("业务部门1数据接口......")
# 业务部门并没有开始执行f1函数
执行结果:
认证成功!
业务部门1数据接口......
日志添加成功

看来没?作者只是概念好了函数,业务部门还并未有调用f一函数呢,程序就把工作全做了。这正是包裹一层函数的原因。

4、装饰器的参数字传送递

 细心的敌人也许已经发现了,上面的例证中,f①函数未有参数,在事实上意况中一定会供给参数的,那参数怎么传递的啊?

 八个参数的景况:

def outer(func):
 def inner(username):
 print("认证成功!")
 result = func(username)
 print("日志添加成功")
 return result
 return inner
@outer
def f1(name):
print("%s 正在连接业务部门1数据接口......"%name)
# 调用方法
f1("jack")

在inner函数的概念部分也助长一个参数,调用func函数的时候传递那些参数,很好通晓呢?可难点又来了,那么此外叁个机关调用的f二有三个参数呢?f三有3个参数呢?你怎么传递?

很简单,我们有*args和**kwargs嘛!号称“万能参数”!轻便修改一下方面包车型客车代码:

def outer(func):
 def inner(*args,**kwargs):
 print("认证成功!")
 result = func(*args,**kwargs)
 print("日志添加成功")
 return result
 return inner
@outer
def f1(name,age):
 print("%s 正在连接业务部门1数据接口......"%name)
# 调用方法
f1("jack",18)

5、更进一步的思辨

 三个函数能够被两个函数装饰吗?能够的!看下边包车型大巴事例!  

def outer1(func):
 def inner(*args,**kwargs):
 print("认证成功!")
 result = func(*args,**kwargs)
 print("日志添加成功")
 return result
 return inner
def outer2(func):
 def inner(*args,**kwargs):
 print("一条欢迎信息。。。")
 result = func(*args,**kwargs)
 print("一条欢送信息。。。")
 return result
 return inner
 @outer1
@outer2
def f1(name,age):
 print("%s 正在连接业务部门1数据接口......"%name)
# 调用方法
f1("jack",18) 
执行结果:
认证成功!
一条欢迎信息。。。
jack 正在连接业务部门1数据接口......
一条欢送信息。。。
日志添加成功

更进一步的,装饰器本身能够有参数吗?能够的!看下边包车型大巴例子:

# 认证函数
def auth(request,kargs):
 print("认证成功!")
# 日志函数
def log(request,kargs):
 print("日志添加成功")
# 装饰器函数。接收两个参数,这两个参数应该是某个函数的名字。
def Filter(auth_func,log_func):
 # 第一层封装,f1函数实际上被传递给了main_fuc这个参数
 def outer(main_func):
 # 第二层封装,auth和log函数的参数值被传递到了这里
 def wrapper(request,kargs):
 # 下面代码的判断逻辑不重要,重要的是参数的引用和返回值
 before_result = auth(request,kargs)
 if(before_result != None):
 return before_result;
 main_result = main_func(request,kargs)
 if(main_result != None):
 return main_result;
 after_result = log(request,kargs)
 if(after_result != None):
 return after_result;
 return wrapper
 return outer
# 注意了,这里的装饰器函数有参数哦,它的意思是先执行filter函数
# 然后将filter函数的返回值作为装饰器函数的名字返回到这里,所以,
# 其实这里,Filter(auth,log) = outer , @Filter(auth,log) = @outer
@Filter(auth,log)
def f1(name,age):
 print("%s 正在连接业务部门1数据接口......"%name)
# 调用方法
f1("jack",18)
运行结果:
认证成功!
jack 正在连接业务部门1数据接口......
日志添加成功

又绕晕了?其实你能够如此清楚,先执行Filter函数,获得它的归来值outer,再进行@outer装饰器语法。

如上便是本文的全体内容,希望本文的始末对大家的求学可能工作能拉动一定的鼎力相助,同时也盼望多多帮衬帮客之家!

装饰器的机能在数不尽言语中都有,名字也不尽一样,其实它显示的是一种设计形式,强调的是…

原文
http://www.cnblogs.com/feixuelove1009/p/5541632.html

二、须求来了

初创公司有N个业务部门,二个基础平台部门,基础平台承担提供底层的效力,如:数据库操作、redis调用、监察和控制API等职能。业务部门使用基础功用时,只需调用基础平台提供的成效就可以。如下:

############### 基础平台提供的功能如下 ###############

 

def f1():

``print 'f1'

 

def f2():

``print 'f2'

 

def f3():

``print 'f3'

 

def f4():

``print 'f4'

 

############### 业务部门A 调用基础平台提供的功能 ###############

 

f1()

f2()

f3()

f4()

 

############### 业务部门B 调用基础平台提供的功能 ###############

 

f1()

f2()

f3()

f4()

近期厂商层次显明的开始展览着,可是,在此以前基础平台的开采职员在写代码时候从不酷爱验证相关的主题素材,即:基础平台的提供的效能能够被任哪个人使用。今后须求对基础平台的有所作用进行重构,为平台提供的有着机能充分验证机制,即:施行效劳前,先实行认证。

装饰器行远自迩,详解Python装饰器由表及里。特别把职业交给 Low
B,他是那般做的:

跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台的功能之前先验证。诶,这样一来基础平台就不需要做任何修改了

当天Low B 被炒鱿鱼了…

那几个把职业付出 Low
BB,他是那般做的:

只对基础平台的代码进行重构,让N业务部门无需做任何修改

############### 基础平台提供的效能如下
###############

def check_login():
    # 验证1
    # 验证2
    # 验证3
    pass

def f1():
    
    check_login()

    print ‘f1’

def f2():
    
    check_login()

    print ‘f2’

def f3():
    
    check_login()

    print ‘f3’

def f4():
    
    check_login()
    
    print ‘f4’

特别看了下Low BBB 的兑现,嘴角漏出了一丝的安详的笑,语重心长的跟Low
BBB聊了个天:

老大说:

写代码要遵守开采封闭原则,尽管在那一个规格是用的面向对象开垦,可是也适用于函数式编制程序,轻易的话,它规定已经落实的意义代码不允许被退换,但能够被扩展,即:

封闭:已兑现的作用代码块

盛开:对扩充开拓

要是将开放封闭原则应用在上述须求中,那么就不相同目的在于函数 f1、f2、f三、f四的当中开始展览更换代码,老董就给了Low BBB三个兑现方案:

def w1(func):

``def inner():

``# 验证1

``# 验证2

``# 验证3

``return func()

``return inner

 

@w1

def f1():

``print 'f1'

@w1

def f2():

``print 'f2'

@w1

def f3():

``print 'f3'

@w1

def f4():

``print 'f4'

对于上述代码,也是单纯对基础平台的代码实行改造,就足以兑今后其余人调用函数
f1 f2 f三 f四 在此以前都开展【验证】操作,并且别的业务部门无需做别的操作。

Low BBB心惊胆战的问了下,那段代码的中间实行原理是何许吧?

可怜正要生气,突然Low BBB的手机掉到地上,恰恰屏保便是Low
BBB的女友照片,老大学一年级看1紧1抖,心花怒放,交定了Low
BBB这些朋友。详细的发端上课了:

单独以f1为例:

def w1(func):

``def inner():

``# 验证1

``# 验证2

``# 验证3

``return func()

``return inner

 

@w1

def f1():

``print 'f1'

当写完那段代码后(函数未被实践、未被实施、未被实行),python解释器就会从上到下解释代码,步骤如下:

  1. def w一(func):  ==>将w壹函数加载到内部存款和储蓄器
  2. @w1

没错,从外表上看解释器仅仅会解释那两句代码,因为函数在平昔不被调用从前其里面代码不会被执行。

从外表上看解释器着实会执行这两句,可是 @w一这一句代码里却有大小说,@函数名 是python的一种语法糖。

如上例@w一内部会实践一下操作:

  • 执行w1函数,并将 @w1 下面的
    函数 作为w1函数的参数,即:@w壹等价于 w壹(f一)
    故此,内部就会去奉行:
        def inner:
            #验证
            return f1()   #
    func是参数,此时 func 等于 f1
    4858.com,    return inner     # 重回的
    inner,inner代表的是函数,非实施函数
    实则就是将原来的 f壹函数塞进其它一个函数中
  • 将执行完的 w1 函数再次回到值赋值给@w1下面的函数的函数名 w1函数的重返值是:
       def inner:
            #验证
            return 原来f1()  # 此处的 f1 代表原本的f一函数
    接下来,将此再次回到值再重复赋值给 f一,即: 新f1 = def inner:
                #验证
                return 原来f1() 
    因此,以往业务部门想要施行 f一 函数时,就会推行 新f一 函数,在 新f1函数内部先进行验证,再实行原来的f一函数,然后将 原来f一 函数的回到值
    重回给了事情调用者。
    如此一来,
    即实施了求证的效力,又实行了本来f一函数的始末,并将原f一函数重返值
    再次回到给业务调用着

Low BBB 你知道了呢?若是没驾驭的话,笔者早晨去你家帮你化解呢!!!

先把上述流程看懂,之后还会连续更新…

盛开:对扩张开辟;封闭:已经达成的代码块

 
装饰器的成效在不少言语中都有,名字也不尽同样,其实它反映的是1种设计形式,重申的是开放封闭原则,越多的用来前期效益升级而不是编写制定新的代码。装饰器不光能装饰函数,也能装饰别的的对象,比如类,但日常,大家以装修函数为例子介绍其用法。要精通在Python中装饰器的原理,供给一步一步来。本文尽量描述得浅显易懂,从最基础的剧情讲起。

三、问答时间

主题材料:被装饰的函数即使有参数呢?

def w1(func):
    def inner(arg1,arg2):
        # 验证1
        # 验证2
        # 验证3
        return func(arg1,arg2)
    return inner

@w1
def f1(arg1,arg2):
    print ‘f1’

三个参数

def w1(func):
    def inner(arg1,arg2,arg3):
        # 验证1
        # 验证2
        # 验证3
        return func(arg1,arg2,arg3)
    return inner

@w1
def f1(arg1,arg2,arg3):
    print ‘f1’

多个参数

主题材料:能够装点具备处理n个参数的函数的装饰器?

def w1(func):

``def inner(``*``args,``*``*``kwargs):

``# 验证1

``# 验证2

``# 验证3

``return func(``*``args,``*``*``kwargs)

``return inner

 

@w1

def f1(arg1,arg2,arg3):

``print 'f1'

主题材料:二个函数能够被几个装饰器装饰吗?

def w1(func):

``def inner(``*``args,``*``*``kwargs):

``# 验证1

``# 验证2

``# 验证3

``return func(``*``args,``*``*``kwargs)

``return inner

 

def w2(func):

``def inner(``*``args,``*``*``kwargs):

``# 验证1

``# 验证2

``# 验证3

``return func(``*``args,``*``*``kwargs)

``return inner

 

 

@w1

@w2

def f1(arg1,arg2,arg3):

``print 'f1'

难题:还有什么更吊的装饰器吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/env python
#coding:utf-8
  
def Before(request,kargs):
    print 'before'
      
def After(request,kargs):
    print 'after'
  
  
def Filter(before_func,after_func):
    def outer(main_func):
        def wrapper(request,kargs):
              
            before_result = before_func(request,kargs)
            if(before_result != None):
                return before_result;
              
            main_result = main_func(request,kargs)
            if(main_result != None):
                return main_result;
              
            after_result = after_func(request,kargs)
            if(after_result != None):
                return after_result;
              
        return wrapper
    return outer
      
@Filter(Before, After)
def Index(request,kargs):
    print 'index'

 

那就是说难点来了如何在不改造原有代码前提下实现质量的增进,装饰器正是多少个很好的用法

装饰器的效应:扩大棉被服装饰器装饰的函数的效益

4、functools.wraps

上述的装饰器即便已经完毕了其相应的成效,即:装饰器内的函数代指了原函数,注意其只是代指而非相等,原函数的元音讯并没有被赋值到装饰器函数内部。例如:函数的笺注音信

def outer(func):
    def inner(*args, **kwargs):
        print(inner.__doc__)  # None
        return func()
    return inner

@outer
def function():
    “””
    asdfasd
    :return:
    “””
    print(‘func’)

无元新闻

 

若是利用@functools.wraps装饰装饰器内的函数,那么就会代指元消息和函数。

def outer(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        print(inner.__doc__)  # None
        return func()
    return inner

@outer
def function():
    “””
    asdfasd
    :return:
    “””
    print(‘func’)

含元新闻

 

 

比如:作者索要在“登6种类前增多验证功效”,在不转移现成的代码该怎么样落到实处?

一、Python的函数相关基础

def test(name):
    print("登录系统___%s" % name)

test("张三")

  第2,必须重申的是python是从上往下1一试行的,而且碰到函数的概念代码块是不会立马实施它的,只有等到该函数被调用时,才会进行当中间的代码块。
           

一)方法一:可能有人会想到利用闭包达成,如

In [2]: def foo():
   ...:     print("foo函数被运行了")
   ...:     
   # 如果就这么样,foo里的语句是不会被执行的;程序只是简单的将定义代码块读入内存中
def outer(func):
    def inner(name):
        print("验证——————————")
        func(name)
    return inner


def test(name):
    print("登录系统___%s" % name)


test = outer(test)
test("张三")

#输出结果
验证——————————
登录系统___张三

再看看,顺序试行的例子:

 

def foo():
    print("我是上面的函数定义!")

def foo():
    print("我是下面的函数定义!")
 
 foo()

运行结果:
 
我是下面的函数定义

那正是说难点来了,那样的话在各种文件不一致作用域里调用test作用在此之前都亟需加上第二二行:
test =
outer(test),那假设如此的内需增添地方较多的话也不具体,且相比难于查找,出现缺漏的结果也得以活动想象。那里就足以应用格局贰

 
可知,因为种种实行的缘故,上面包车型大巴foo将上边的foo覆盖了。因而,在Python中代码的放置地点是有供给的,无法自由摆布,函数体要放在被调用的口舌在此之前。

二)方法2:使用装饰器

  其次,大家还要先搞通晓几样东西:函数名、函数体、再次来到值,函数的内部存款和储蓄器地址、函数名加括号、函数名被作为参数、函数名加括号被作为参数、再次来到函数名、重返函数名加括号。             

 1 def outer(func):
 2     def inner(name):
 3         print("验证——————————")
 4         func(name)
 5     return inner
 6 
 7 
 8 @outer
 9 def test(name):
10     print("登录系统___%s" % name)
11 
12 
13 test("张三")
14 
15 #输出结果
16 验证——————————
17 登录系统___张三

对于如下的函数:      

 

In [10]: def foo():
    ...:     print("让我们干点啥!")
    ...:     return "ok"
    ...: 

In [12]: foo
Out[12]: <function __main__.foo>    
        
In [11]: foo()
让我们干点啥!
Out[11]: 'ok'

此间能够见到只须要在test函数前增加 @outer 
就能够落成,也得以这么理解  @outer –> test = outer(test)

函数名: foo

当装饰的函数有参数服装饰器内部也亟须求定义形参,即
第 二 行,在第 四 行时也必须传递参数举行调用

函数体: 第1-3行

多少个函数使用同样的印证功用时也是这样

再次来到值: 字符串“ok” 假设不显得给出return的目的,那么暗许重回None

 1 def outer(func):
 2     def inner(name):
 3         print("验证——————————")
 4         func(name)
 5     return inner
 6 
 7 
 8 @outer
 9 def test1(name):
10     print("登录系统___%s" % name)
11 
12 
13 @outer
14 def test2(name):
15     print("查询余额___%s" % name)
16 
17 
18 test1("张三")
19 test2("李四")
20 
21 #输出结果
22 验证——————————
23 登录系统___张三
24 验证——————————
25 查询余额___李四

函数的内部存款和储蓄器地址: 当函数体被读进内部存款和储蓄器后的保留地点,它由标记符即函数名foo引用,也等于说foo指向的是函数体在内部存款和储蓄器内的保留地点。

 

函数名加括号:foo(),函数的调用方法,只有见到那一个括号,程序会依据函数名从内部存款和储蓄器中找到函数体,然后试行它

2、四个装饰器

再看上边那几个例子:

 1 def outer1(func):
 2     print("_____装饰器outer1____")
 3     def inner1():
 4         print("———inner1———————")
 5         func()
 6     return inner1
 7 
 8 def outer2(func):
 9     print("_____装饰器outer2____")
10     def inner2():
11         print("———inner2———————")
12         func()
13     return inner2
14 
15 @outer1
16 @outer2
17 def test():
18     pass
19 
20 test()
21 
22 #输出结果
23 _____装饰器outer2____
24 _____装饰器outer1____
25 ———inner1———————
26 ———inner2———————
In [15]: def outer(func):
    ...:     def inner():
    ...:         print('我是内层函数!')
    ...:     return inner
    ...: 

In [16]: def foo():
    ...:     print('我是原始函数')
    ...:     

In [18]: outer(foo)
Out[18]: <function __main__.outer.<locals>.inner>

In [19]: outer(foo)()
我是内层函数!

In [20]: outer(foo())
我是原始函数
Out[20]: <function __main__.outer.<locals>.inner>

In [21]: outer(foo())()
我是原始函数
我是内层函数!

 

  在python中,1切都以对象,函数也不例外。由此得以将函数名,甚至函数名加括号举行调用的方法作为另三个函数的参数。

从结果也足以见见,当有多个装饰器时,装饰器是从内往外装饰,即:一)@outer二–> 
 test = outer贰(test)   2)
@outer1–> test = outer一(test) 

 
下面代码中,outer和foo是多个函数,outer(foo)表示将foo函数的函数名当做参数字传送递给outer函数并试行outer函数;outer(foo())表示将foo函数试行后的重返值当做参数字传送递给outer函数并试行outer函数,由于foo函数未有点名重返值,实际上它传递给了outer函数三个None。注意当中的差距,有未有括号是重点!

奉行到,壹伍,1陆初步装修时就会有出口输出二叁,二4行,原因请看上面1)二),在装修时也会推行outer二(),outer壹()五个函数,所以会有出口结果

 
同样,在outer函数内部,重返了3个inner,它是在outer函数内部定义的3个函数,注意,由于inner前边未有加括号,所以回来的是inner的函数体,实际上也正是inner那些名字,一个简便的引用而已。那么,借使outer函数重临的是inner()呢?以后您应该已经很了然了,它会先举行inner函数的情节,然后回来个None给outer,outer再把那几个None再次回到给调用它的靶子。

三、装饰器带重返值

  
请记住,函数名、函数加括号能够被看成参数字传送递,也能够被看作再次来到值return,有未有括号是多个完全不一致的意思!
          

 1 def outer(func):
 2     def inner(a, b):
 3         ret = func(a, b)
 4         return ret
 5     return inner
 6 
 7 @outer
 8 def test(a, b):
 9     return a + b
10 
11 ret = test(5, 2)
12 print("-----%d" % ret)
13 
14 #输出结果
15 -----7

贰、装饰器的使用情状

当装饰的函数有重返值时要求将重返值重临,如第 3,四行

  装饰器通常用于在不改造原有函数代码和意义的气象下,为其加多额外的效率。比如在原函数推行前先实施点什么,在实施后实践点什么。

四、通用装饰器

 
让大家由此三个例证来看看,装饰器的使用处境和体现的设计方式。(抱歉的是自家设计不出更加好的场馆,只可以引用武大神的案例加以演绎)

 1 def outer(func):
 2     def inner(*args, **kwargs):
 3         ret = func(*args, **kwargs)
 4         return ret
 5     return inner
 6 
 7 @outer
 8 def test1(a):
 9     return a ** 2
10 
11 @outer
12 def test2(a, b, c):
13     return a * b * c
14 
15 print(test1(3))
16 print(test2(2, 3, c=5))
17 
18 #输出结果
19 9
20 30

 
有一个大商厦,下属的底蕴平台部负责内部应用程序及API的支出,有好四个业务部门负责分化的事业,他们分别调用基础平台部提供的区别函数处理自身的业务,情形如下:

当八个函数都以用同1个装饰器的时候,参数不均等的主题素材,就的采用通用装饰器来消除,通用即全数的函数都适用的意思。

 # 基础平台部门开发了上百个函数
 def f1():
     print("业务部门1数据接口......")
 
 def f2():
     print("业务部门2数据接口......")
 
 def f3():
     print("业务部门3数据接口......")
  .
  .
  .
  
 def f100():
     print("业务部门100数据接口......")
     
 # 各部门分别调用
 f1()
 f2()
 f3()
 f100()

如上所示,test一与test2函数的参数不等同,也足以用一样的装饰器装饰,第一,③行化解

 
由于公司在创业初期,基础平台部开拓那么些函数时,由于各类缘由,比如时间,比如考虑不周等等,未有为函数调用进行安全注脚。以后,平台部老板决定弥补那几个毛病,于是:

伍、带参数的装饰器

先是回,老板叫来了1个运行工程师,工程师跑上跑下各个机关进行通报,让他们在代码里丰盛认证功能,可是,当天他被开掉了。

 1 def f1(flag=1):
 2     def outer(func):
 3         def inner(*args, **kwargs):
 4             if (flag == 2):
 5                 print("______%d" % flag)
 6                 ret = func(*args, **kwargs)
 7             else:
 8                 print("______%d" % flag)
 9                 ret = func(*args, **kwargs)
10             return ret
11         return inner
12     return outer
13 
14 @f1(2)
15 def test(a):
16     return a ** 2
17 
18 print(test(3))
19 
20 #输出结果
21 ______2
22 9

其次回:老董又叫来了3个运行工程师,工程师用shell写了个复杂的本子,勉强达成了效果。但她急忙就重回接着做运营了,不会支出的运营不是辛亏维….

当当须要经过分化参数判定装饰器该兑现的两样结果时,带参数的装饰器就出现了,如上所示,在装饰器最外层在嵌套四个函数用与吸收接纳参数,通过第四 行判定参数选拔相应的意义

其一次:首席营业官叫来了1个python自动化开垦工程师,男士是那般干的:  
 只对基础平台的代码进行重构,让N个业务部门无需做其它更动。那哥俩异常快也被开了,连续运输行也没得做。

上面说说 1四行  的施行流程  1)@f一(2)
–> f壹(二)  实践 f1函数并传值 2 ;   二)f一 函数重回 outer
—>@outer;三)@outer –> test = outer(test)

def f1():
    #加入认证程序代码
    print("业务部门1数据接口......")

def f2():
    # 加入认证程序代码
    print("业务部门2数据接口......")

def f3():
    # 加入认证程序代码
    print("业务部门3数据接口......")
.
.
.
    
def f100():
    #加入认证程序代码
    print("业务部门100数据接口......")
    
# 各部门分别调用
f1()
f2()
f3()
f100()

 

第7遍:主管又换了个工程师,他是这么干的:定义个说明函数,原来其余的函数调用它,代码如下框。可是,主任依然比不上意,不过这三遍他表达了为何。总经理说:写代码要依据开放封闭原则,固然在这几个条件主借使针对性面向对象开垦,可是也适用于函数式编制程序,简单的说,它规定已经落实的效益代码内部不允许被修改,但表面能够被增加

如上就是自身对装饰器的有个别私人住房掌握了

封闭:已兑现的职能代码块;

绽开:对扩张开放。

万1将绽放封闭原则应用在上述供给中,那么就不允许在函数 f壹、f二、f叁……f拾0的个中实行代码  
修改。遗憾的是,工程师没有精美的女对象,所以高速也被开掉了。

def login():
    print("认证成功!")
    
    
def f1():
    login()
    print("业务部门1数据接口......")
def f2():
    login()
    print("业务部门2数据接口......")
def f3():
    login()
    print("业务部门3数据接口......")
def f100():
    login()
    print("业务部门100数据接口......")

# 各部门分别调用
f1()
f2()
f3()
f100()

第伍次:已经远非时间让主持找外人来干那活了,他调节亲自上阵,并且打算在函数试行后再追加个日志功用。主任是如此想的:不会装饰器的掌管不是好码农!要不为何笔者能当主持,你只可以被管啊?嘿嘿。他的代码如下:

def outer(func):
    def inner():
        print("认证成功!")
        result = func()
        print("日志添加成功")        
        return result    
    return inner
    
@outer
def f1():
    print("业务部门1数据接口......")
    
@outer
def f2():
    print("业务部门2数据接口......")
    
@outer
def f3():
    print("业务部门3数据接口......")
    
@outer
def f100():
    print("业务部门100数据接口......")
    
# 各部门分别调用
f1()
f2()
f3()
f100()

 
对于上述代码,也是仅需对基础平台的代码举行拓展,就足以兑以往其余单位调用函数
f壹 f2 f三 f十0
从前都进行认证操作,在操作截至后保存日志,并且其余业务部门无需他们友善的代码做任何改动,调用格局也不用变。“高管”写完代码后,认为独乐了不及众乐乐,打算装X一下,于是写了篇博客将经超过实际行了详实的证明。

三、装饰器的在那之中原理

上边大家以f壹函数为例举办求证:

In [23]: def outer(func):
    ...:     def inner():
    ...:         print('认证成功!')
    ...:         result = func()
    ...:         print('日志添加成功')
    ...:         return result
    ...:     return inner
    ...: 

In [24]: @outer        # f1 = outer(f1) = inner
    ...: def f1():
    ...:     print('业务部门1数据接口...')
    ...:     

In [25]: f1()
认证成功!
业务部门1数据接口...
日志添加成功

使用大家在第二局地介绍的学问来分析一下方面那段代码:

  1. 次第开端运转,从上往下编写翻译,读到def
    outer(func):的时候,发现这是个“一等百姓”->函数,于是把函数体加载到内部存款和储蓄器里,然后过。

  2. 读到@outer的时候,程序被@那个语法糖吸引住了,知道这是个装饰器,按规矩要立时实行的,于是程序初叶运维@前面这二个名字outer所定义的函数。(相信未有人会愚拙的将@outer写到其他地点,它不得不放在被点缀的函数的顶端近年来处,不要空行。)

  3. 程序再次回到到outer函数,起初执行李装运饰器的语法规则,那有个别条条框框是定死的,是python的“法律”,不要问何故。

    规则是:棉被服装饰的函数的名字会被视作参数字传送递给装修函数。装饰函数试行它和谐内部的代码后,会将它的重返值赋值给被点缀的函数。  
          

那里面必要专注的是:

  • @outer和@outer()有分别:未有括号时,outer函数照旧会被奉行,那和价值观的用括号工夫调用函数不一样,要求尤其注意!那么有括号吧?那是装饰器的高端级用法了,未来会介绍。

  • 是f一那一个函数名(而不是f一()那样被调用后)当做参数字传送递给装饰函数outer,相当于:func

    f一,@outer等于outer(f一),实际上传递了f1的函数体,而不是实行f一后的重返值。

  • outer函数return的是inner那些函数名,而不是inner()那样被调用后的重返值。

 
假设你对第二部分函数的基础知识有旁观众清的问询,那么地方的剧情你应当很容易精通。

  四.
顺序起初实践outer函数内部的剧情,一同初它又遇上了3个函数,很绕是啊?当然,你能够在
inner函数前后布署点别的代码,但它们不是重点,而且有点小麻烦,上面会分解。inner函数定义块被先后观望到后不会应声实行,而是读入内部存款和储蓄器中(那是潜规则)。

  伍. 再往下,碰着return inner,重临值是个函数名,并且那么些函数名会被赋值给f一以此被点缀的函数,也等于f一

inner。依据后面包车型地铁学问,大家知道,此时f一函数被新的函数inner覆盖了(实际上是f壹以此函数名改成成指向inner那么些函数名指向的函数体内部存款和储蓄器地址,f一不再指向它原先的函数体的内部存款和储蓄器地址),再未来调用f1的时候将实践inner函数内的代码,而不是原先的函数体。那么先前的函数体去哪了?还记得我们将f1当做参数字传送递给func这几个形参么?func那一个变量保存了老的函数在内部存款和储蓄器中的地址,通过它就能够执行老的函数体,你能在inner函数里看看result =
func()这句代码,它就是那样干的!

  6.接下来,还未曾实现。当业务部门,依旧通过f一()的办法调用f壹函数时,推行的就不再是老的f1函数的代码,而是inner函数的代码。在本例中,它首先会打字与印刷个“认证成功”的唤起,很驾驭你能够换到自由的代码,那只是个示范;然后,它会施行func函数并将重回值赋值个变量result,那么些func函数就是老的f一函数;接着,它又打印了“日志保存”的唤醒,那也只是个示范,能够换到其它你想要的;最终回到result那一个变量。大家在业务部门的代码上能够用
r = f一()的法子接受result的值。

 
柒.之上流程走完后,你应有看出来了,在未曾对业务部门的代码和接口调用格局做别的退换的还要,也尚无对基础平台部原有的代码做内部修改,仅仅是增多了贰个装潢函数,就完成了大家的须求,在函数调用前先证实,调用后写入日志。那正是装饰器的最大功效。

难题:那么为何大家要搞2个outer函数八个inner函数这么复杂呢?一层函数不行啊?
 

答:请留意,@outer那句代码在程序试行到那边的时候就会活动推行outer函数内部的代码,假若不封装一下,在业务部门还未举行调用的时候,就施行了些什么,那和初衷有点不符。当然,假如你对这么些有要求也不是这几个。请看上面包车型客车例证,它唯有1层函数。
 

In [33]: def outer(func):
    ...:     print('认证成功!')
    ...:     result = func()
    ...:     print('日志添加成功')
    ...:     return result
    ...: 

In [34]: @outer
    ...: def f1():
    ...:     print('业务部门1数据接口...')
    ...:     
认证成功!
业务部门1数据接口...
日志添加成功

# 业务部门并没有执行f1函数

 
看到没?笔者只是概念好了函数,业务部门还平素不调用f1函数呢,程序就把职业全做了。这正是包裹1层函数的缘由。

叁、装饰器的参数字传送递

1、被点缀函数的参数字传送递

 
细心的仇敌大概已经意识了,下边包车型客车事例中,f壹函数未有参数,在骨子里情况中势必会需求参数的,那参数怎么传递的啊?

贰个参数的处境:

In [13]: def outer(func):
    ...:     def inner(username):
    ...:         print('认证成功')
    ...:         result = func(username)
    ...:         print('日志添加成功')
    ...:         return result
    ...:     return inner
    ...: 

In [14]: @outer
    ...: def f1(name):
    ...:     print("{}正在连接业务部门1数据接口。。。".format(name))
    ...:     

In [15]: f1("jack")
认证成功
jack正在连接业务部门1数据接口。。。
日志添加成功

 
在inner函数的概念部分也助长三个参数,调用func函数的时候传递那一个参数,很好精通吧?可难题又来了,那么其它3个机关调用的f二有二个参数呢?f三有2个参数呢?你怎么传递?

很简单,我们有*args和**kwargs嘛!号称“万能参数”!

简单修改一下方面包车型客车代码:

In [17]: def outer(func):
    ...:     def inner(*args, **kwargs):
    ...:         print('认证成功')
    ...:         result = func(*args, **kwargs)
    ...:         print('日志添加成功')
    ...:         return result
    ...:     return inner
    ...: 

In [18]: @outer
    ...: def f1(name, age):
    ...:     print("{}正在连接业务部门1数据接口。。。".format(name))
    ...:     

In [19]: f1("jack", 18)
认证成功
jack正在连接业务部门1数据接口。。。
日志添加成功

贰、装饰器本人的参数

  装饰器自个儿的参数?装饰器自个儿的参数不是棉被服装饰的函数吗?

    
那装饰器如何传播本人的参数呢?

In [31]: # 认证函数

In [32]: def auth(request, kwargs):
    ...:     print("认证成功!")
    ...:     

In [33]: # 日志函数

In [34]: def log(request, kwargs):
    ...:     print("日志添加成功!")
    ...:     

In [35]: # 装饰器函数,接收2个参数,这两个参数应该是某个函数的名字。

In [41]: def Filter(auth_func, log_func):
    ...:     # 第一层封装,auth和log函数的参数值被传递到了这里
    ...:     def outer(main_func):
    ...:         # 第二层封装,f1函数实际上被传递给了main_func这个参数
    ...:         def wrapper(request, kargs): 
    ...:         # 下面代码的判断逻辑不重要,重要的是参数的引用和返回值
    ...:             before_result = auth(request, kargs) 
    ...:             if(before_result  None):
    ...:                 return before_result
    ...:             main_result = main_func(request, kargs)
    ...:             if(main_result != None):
    ...:                 return main_result
    ...:             after_result = log(request, kargs)
    ...:             if(after_result != None):
    ...:                 return after_result
    ...:         return wrapper
    ...:     return outer
    ...: 
    # 注意了,这里的装饰器函数有参数哦,它的意思是先执行Filter函数
    # 然后将Filter函数的返回值作为装饰器函数的名字返回到这里,所以,
    # 其实这里等价于,Filter(auth,log) = outer , @Filter(auth,log) =  @outer
        #  也等价于f1=Filter(auth, log)(f1)=@outer=wrapper
    
In [44]: @Filter(auth, log)  
    ...: def f1(name, age):
    ...:     print("{} 正在连接业务部门1数据接口。。。".format(name))
    ...:     

In [45]: f1("jack", 18)
认证成功!
jack 正在连接业务部门1数据接口。。。
日志添加成功!

    又绕晕了?事实上您能够那样精晓,先进行Filter函数,得到它的回来值outer,再执行@outer装饰器语法。

二个函数重返3个不带参数的装饰器就是带参数的装饰器

因为装饰器自己的参数无法一直传入所以在装饰器在装饰器外再封装一层函数的目地就是为了给装饰器传参,

4、更进一步的讨论

三个函数能够被多个函数装饰吗?能够的!看上面包车型客车例子!

In [25]: def outer1(func):  
    ...:     def inner(*args, **kwargs):
    ...:         print('认证成功')          
    ...:         result = func(*args, **kwargs)
    ...:         print('日志添加成功')      
    ...:         return result
    ...:     return inner
    ...: 
In [25]: def outer1(func):
    ...:     def inner(*args, **kwargs):
    ...:         print('认证成功')
    ...:         result = func(*args, **kwargs)
    ...:         print('日志添加成功')
    ...:         return result
    ...:     return inner
    ...: 

In [26]: def outer2(func):
    ...:     def inner(*args, **kwargs):
    ...:         print('一条欢迎信息。。。')
    ...:         result = func(*args, **kwargs)
    ...:         print('一条欢送信息。。。')
    ...:         return result
    ...:     return inner
    ...: 

In [27]: @outer1
    ...: @outer2
    ...: def f1(name, age):
    ...:     print("{} 正在连接业务部门1数据接口。。.".format(name))
    ...:     

In [28]: f1("jack", 18)
认证成功
一条欢迎信息。。。
jack 正在连接业务部门1数据接口。。.
一条欢送信息。。。
日志添加成功

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有