>

Python装饰器的易懂明了

- 编辑:www.bifa688.com -

Python装饰器的易懂明了

1、什么是装饰器

当大家搞好一个成品今后,供给对它举办连发地维护,对少数函数增添一些功力。那一年假诺去修改源代码将是拾叁分不正好的。(原因:一.原则仲春经写好的函数尽量不去修改它,因为只要修改或然会产生不可预言的荒唐产生可能下跌稳固性。二.函数或许被调用大多过多次,假若修改函数有非常大希望会促成调用方式产生变动,会有恢宏的更改职业。)所以,装饰器就应际而生了。它能够完毕为函数扩大新职能的还要而不修改函数自个儿的源代码。

转发来源于:http://blog.csdn.net/u013471155

装饰器

二、装饰器的原则

一.不能够修改被修饰函数的源代码

2.不能够修改被修饰函数的调用格局

在就学Python的进度中,笔者深信有众四人和本身同一,对Python的装饰器从来认为很狐疑,笔者也是可疑了旷日持久,并经过观念和查看才能略有领会,小编梦想以下的剧情会对您有帮扶,作者也努力通太早先的主意使得对Python装饰器的领会更加的不亦乐乎。在文中如有遗漏和不足,迎接交换和带领。

一.怎么要选拔装饰器?

三、实现装饰器的知识储备

壹.函数即“变量”:定义三个函数,也就是将函数体赋值给函数名那些变量。

贰.高阶函数:满意以下八个尺码之1的函数即为高阶函数:1把函数名当加强参传给另3个函数二再次来到值中带有函数名

三.嵌套函数:函数是能够嵌套的

洋美国人对装饰器难以精通,原因是出于以下3点内容并没有搞精晓:

在产品晋级中,有成千上百个函数,要为那些函数扩展一些意义,纵然1个个函数的改变,岂不是浪费能源,浪费时间,那是装饰器的便宜就表现了,它可以在长期内到位你对众多函数的修改。

4、使用装饰器

高阶函数 嵌套函数=装饰器

装饰器的真相是函数,作用是为任何函数加多附加成效

#现在有两个函数,分别有各自的功能,需求是计算每个函数运行的时间

def text1():
    time.sleep(2)
    print("this is text1")

def text2(*args,**kwargs):
    time.sleep(2)
    print("this is text2",args[0],args[1])

能够写装饰器:

def timer(func):
    def deco(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)
        end_time=time.time()
        print("the function tun time is {0}".format(end_time-start_time))
    return deco

里面func()函数是原函数真正的实践进度,deco函数内任何的口舌都以新扩张效果的口舌

装饰器名字为timer

写好装饰器就行了吗?不是,还差一个手续,在原函数上方增添一条语句,整合到一块:

def timer(func):
    def deco(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)
        end_time=time.time()
        print("the function tun time is {0}".format(end_time-start_time))
    return deco

@timer  #此操作的作用是将装饰器的功能加到原函数上 相当于text1=timer(text1)
def text1():
    time.sleep(2)
    print("this is text1")


@timer #没有@timer语句则不会将装饰器功能加上
def text2(*args,**kwargs): #如果原函数有参数,需要在装饰器中deco上加参数
    time.sleep(2)
    print("this is text2",args[0],args[1])

text1() #不改变调用方式,且增加了新功能
text2(29000,"hello")

装饰器分析:deco函数用来兑现新扩充效益,timer函数和@timer语句用来使调用格局不发出改换

施行结果:

必发88手机版 1

  1. 关于函数“变量”(或“变量”函数)的精晓
  2. 关于高阶函数的知道
  3. 至于嵌套函数的驾驭

2.如何是装饰器?

伍、带参数的装饰器

此地说的不是原函数带参数,而是装饰器函数本身带参数

当今有供给,用装饰器完成登录进度,并且有三种不一致的登录格局:使用本地数据登录和行使云端数据登入

#现在有三个网页,其中登陆home时用本地数据,登陆bbs时用云端数据

def index():
    print("welcome to index page")

def home():
    print("welcome to home page")
    return "form home"

def bbs():
    print("welcome to bbs page")

要求装饰器:

name="alex"
word="123"
def auth(auth_type):
    def auth_out(func):
        def deco(*args,**kwargs):
            if auth_type=="local": #如果使用本地数据
                uesrname=input("请输入用户名:").strip()
                password=input("请输入密码:").strip()
                if uesrname==name : # 如果用户存在
                    if password==word:#如果密码正确
                        print("使用本地数据")
                        print("欢迎进入!")
                        return func(*args,**kwargs) #进入home界面
                    else:
                        print("密码错误")
                else:
                    print("用户不存在")
            elif auth_type=="cloude": #如果使用云端数据
                print("使用云端数据")
                print("欢迎")
                func() # 进入bbs界面
        return deco
    return auth_out

def index():
    print("welcome to index page")

@auth(auth_type="local") #登录home界面使用本地用户数据,在auth()内传参
def home():
    print("welcome to home page")
    return "form home"

@auth(auth_type="cloude") #登录bbs界面使用云端用户数据
def bbs():
    print("welcome to bbs page")

index()
print(home())
bbs()

实则正是将原本的装饰器外层又嵌套了一层函数,用来传递参数。@语句也改为了@auth(auth_type=" ")

运维结果:

必发88手机版 2

 

那就是说只要能对上述的主题素材逐1攻破,同时依照装饰器的中央规则,相信会对装饰器有个很好的知情的。那么大家先来看以下装饰器的指标及其原则。

装饰器也是函数,是装饰别的函数的函数(为别的函数扩展效益)

1、装饰器

装饰器实际上正是为了给某先后扩充作用,但该程序已经上线或已经被应用,那么就无法大量的修改源代码,那样是不科学的也是不现实的,因为就发生了装饰器,使得其满意:

  1. 无法修改棉被服装饰的函数的源代码
  2. 无法修改被点缀的函数的调用格局
  3. 知足一、二的气象下给程序扩张功能

那便是说根据要求,同时知足了那叁点原则,那才是大家的指标。因为,上面大家从化解这三点原则入手来领悟装饰器。

等等,作者要在急需从前先说装饰器的规则组合:

三.装饰器的条件:

< 函数 实参高阶函数 重返值高阶函数 嵌套函数 语法糖 = 装饰器 >

本条姿势是贯穿装饰器的神魄所在!

  a.不可能改改原函数的代码

二、须求的落到实处

假诺有代码:

improt time
def test():
    time.sleep(2)
    print("test is running!")
test()

很驾驭,这段代码运营的结果必然是:等待约2秒后,输出

test is running

  • 那就是说须求在满足三尺度的底蕴上,给程序增多总结运营时刻(贰 second)功能

在行动从前,大家先来看一下篇章发轫提到的案由1(关于函数“变量”(或“变量”函数)的明亮)

  b.不更改原函数的调用形式

2.1、函数“变量”(或“变量”函数)

假使有代码:

x = 1
y = x
def test1():
    print("Do something")
test2 = lambda x:x*2

那么在内存中,应该是这么的:

必发88手机版 3

image

很让人侧目,函数和变量是一样的,都是“多少个名字对应内部存款和储蓄器地址中的一些内容
那正是说根据那样的条件,我们就能够精通七个业务:

  1. test1表示的是函数的内部存储器地址
  2. test一()正是调用对在test一那一个位置的内容,即函数

要是那八个难点得以领略,那么大家就足以进来到下二个缘由(关于高阶函数的接头)

四.装饰器的学识储备:

贰.2高阶函数

那么对于高阶函数的款式得以有二种:

  1. 把一个函数名当作实参传给别的3个函数(“实参高阶函数”)
  2. 再次回到值中包括函数名(“重回值高阶函数”)

那么这里面所说的函数名,实际上正是函数的地址,也得以认为是函数的一个标签而已,并不是调用,是个名词。假诺可以把函数名当压实参,那么也正是说可以把函数字传送递到另三个函数,然后在另二个函数里面做一些操作,依照那么些分析来看,那岂不是满意了装饰器3规格中的第一条,即不修改源代码而扩张效果。那大家看来一下切实可行的做法:

要么针对地点这段代码:

improt time

def test():
    time.sleep(2)
    print("test is running!")

def deco(func):  
    start = time.time()
    func() #2
    stop = time.time()
    print(stop-start)

deco(test) #1

我们来看一下这段代码,在#一处,大家把test当作实参传递给形参func,即func=test。注意,这里传递的是地点,也便是此时func也针对了事先test所定义的十分函数体,能够说在deco()内部,func正是test。在#2处,把函数名背后加上括号,正是对函数的调用(试行它)。因而,这段代码运维结果是:

test is running!
the run time is 3.0009405612945557

大家来看仿佛是达标了要求,即实行了源程序,同时也增大了计时成效,然而那只满意了尺度壹(无法改改被装饰的函数的源代码),但那修改了调用格局。假如不变调用格局,那么在那样的次序中,棉被服装饰函数就无法传递到另贰个装饰函数中去。

那即是说再思量,若是不修改调用格局,正是毫无疑问要有test()那条语句,那么就用到了第二种高阶函数,即重返值中含有函数名

正如代码:

improt time

def test():
    time.sleep(2)
    print("test is running!")

def deco(func):  
    print(func)
    return func 
t = deco(test) #3
#t()#4

test()

咱俩看这段代码,在#3处,将test传入deco(),在deco()里面操作之后,最终回到了func,并赋值给t。因而这里test => func => t,没什么不相同的函数体。末了在#四处保存了本来的函数调用方式。
观看这里确定会略带纳闷,我们的急需不是要总括函数的运维时间么,怎么改成输出函数地址了。是因为,单独行使第二张高阶函数(重返值中带有函数名)的章程,并且保留原函数调用格局,是心有余而力不足计时的。假如在deco()里计时,显明会进行一次,而外面已经调用了test(),会另行实行。这里只是为着注脚第三种高阶函数的盘算,上边才真的进入入眼。

  a.函数即“变量”

二.3 嵌套函数

嵌套函数指的是在函数内部定义几个函数,而不是调用,如:

def func1():
    def func2():
        pass
而不是
def func1():
    func2()

其它还有1个题外话,函数只好调用和它同等第以及上级的变量或函数。也正是说:里面包车型客车能调用和它缩进同样的和他外表的,而内部的是无力回天调用的。

这正是说大家再回到大家事先的老大供给,想要总结程序运营时间,并且满意三原则。

代码:

improt time

def timer(func) #5
    def deco():  
        start = time.time()
        func()
        stop = time.time()
        print(stop-start)
    return deco

test = timer(test) #6

def test():
    time.sleep(2)
    print("test is running!")   
test() #7

这段代码或者会稍为吸引,怎么突然多了这么多,一时先接受它,解析一下再来讲为什么是那般。

  b.高阶函数

首先,在#必发88手机版,陆处,把test作为参数字传送递给了timer(),此时,在timer()内部,func

test,接下去,定义了三个deco()函数,当未有调用,只是在内部存款和储蓄器中保存了,并且标签为deco。在timer()函数的终极回来deco()的地方deco。

然后再把deco赋值给了test,那么此时test已经不是本来的test了,也正是test原来的这些函数体的竹签换掉了,换到了deco。那么在#柒处调用的实际是deco()。

那么这段代码在真相上是修改了调用函数,但在表面上并未有修改调用方式,而且完毕了附加功用。

  c.嵌套函数

那么通俗一点的掌握正是:

把函数看成是盒子,test是小盒子deco是中盒子timer是大盒子。程序中,把小盒子test传递到大盒子temer中的中盒子deco,然后再把中盒子deco拿出去,展开看看(调用)

那般做的原由是:

我们要保留test(),还要总括时间,而test()只能调用一回(调用几回运营结果会改动,不满意),再依赖函数即“变量”,那么就能够通过函数的法门来回闭包。于是乎,就想到了,把test传递到有个别函数,而那个函数内恰巧内嵌了四个内函数,再依附内嵌函数的功能域(能够访问同级及以上,内嵌函数能够访问外部参数),把test包在这些内函数个中,一齐回去,最终调用那几个再次回到的函数。而test传递进入之后,再棉被服装进出来,分明test函数未有弄丢(在包装里),那么外面剩下的那个test标签正好能够代表那个包裹(内含test())。

必发88手机版 4

image

从那之后,一切皆合,马到成功,单只差一步。

a.函数即“变量”

三、 真正的装饰器

根据以上分析,装饰器在装饰时,要求在每种函数前边加上:

test = timer(test)

鲜明某个麻烦,Python提供了一种语法糖,即:

@timer

那两句是等价的,只要在函数前拉长那句,就能够完成装饰功用。

如上为无参情势

在概念函数时,系统将函数体内的函数过程作为字符串保存在内部存款和储蓄器中,当作四个变量累积,只是把地址赋给了函数名,在调用函数时,再去解释函数进度

四、装饰有参函数

improt time

def timer(func)
    def deco():  
        start = time.time()
        func()
        stop = time.time()
        print(stop-start)
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")   
test() 

对于三个其实难点,往往是有参数的,假设要在#8处,给被修饰函数加上参数,分明这段程序会报错的。错误原因是test()在调用的时候缺少了2个地方参数的。而我辈清楚test = func = deco,因而test()=func()=deco()
,那么当test(parameter)有参数时,就无法不给func()和deco()也拉长参数,为了使程序尤其有增加性,因而在装饰器中的deco()和func(),加如了可变参数*agrs和 **kwargs。

总体代码如下:

improt time

def timer(func)
    def deco(*args, **kwargs):  
        start = time.time()
        func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")   
test() 

那么大家再思考个难题,即使原函数test()的结果有重回值呢?比方:

def test(parameter): 
    time.sleep(2)
    print("test is running!")   
    return "Returned value"

那么面前蒙受那样的函数,要是用地点的代码来装点,最终一行的test()实际上调用的是deco()。有人恐怕会问,func()不就是test()么,怎么没再次来到值呢?

实际是有再次来到值的,可是回去值重临到deco()的中间,而不是test()即deco()的重返值,那么就要求再回去func()的值,因而正是:

def timer(func)
    def deco(*args, **kwargs):  
        start = time.time()
        res = func(*args, **kwargs)#9
        stop = time.time()
        print(stop-start)
        return res#10
    return deco

其中,#9的值在#10处返回。

总体程序为:

improt time

def timer(func)
    def deco(*args, **kwargs):  
        start = time.time()
        res = func(*args, **kwargs)
        stop = time.time()
        print(stop-start)
        return res 
    return deco

@timer
def test(parameter): #8
    time.sleep(2)
    print("test is running!")   
    return "Returned value"
test() 

b.高阶函数

伍、带参数的装饰器

又追加了1个急需,二个装饰器,对区别的函数有例外的装修。那么就须求知道对哪些函数采纳哪个种类装饰。由此,就须求装饰器带多少个参数来标志一下。比方:

@decorator(parameter = value)

例如有五个函数:

def task1():
    time.sleep(2)
    print("in the task1")

def task2():
    time.sleep(2)
    print("in the task2")

task1()
task2()

要对那八个函数分别计算运转时刻,然则供给计算之后输出:

the task1/task2 run time is : 2.00……

于是就要构造3个装饰器timer,并且要求报告装饰器哪个是task壹,哪个是task二,也正是要这么:

@timer(parameter='task1') #
def task1():
    time.sleep(2)
    print("in the task1")

@timer(parameter='task2') #
def task2():
    time.sleep(2)
    print("in the task2")

task1()
task2()

那么方法有了,但是大家需求思量怎么把这些parameter参数字传送递到装饰器中,大家将来的装饰器,都是传递函数名字进去,而此番,多了四个参数,要如何是好吧?
于是,就想开再加1层函数来经受参数,依照嵌套函数的定义,要想进行内函数,将在先进行外函数,手艺调用到内函数,那么就有:

def timer(parameter): #
    print("in the auth :", parameter)

    def outer_deco(func): #
        print("in the outer_wrapper:", parameter)

        def deco(*args, **kwargs):

        return deco

    return outer_deco

首先timer(parameter),接收参数parameter=’task10分之5’,而@timer(parameter)也正好带了括号,那么就能够进行这一个函数, 那么就是也正是:

timer = timer(parameter)
task1 = timer(task1)

末尾的周转就和一般的装饰器同样了:

import time

def timer(parameter):

    def outer_wrapper(func):

        def wrapper(*args, **kwargs):
            if parameter == 'task1':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task1 run time is :", stop - start)
            elif parameter == 'task2':
                start = time.time()
                func(*args, **kwargs)
                stop = time.time()
                print("the task2 run time is :", stop - start)

        return wrapper

    return outer_wrapper

@timer(parameter='task1')
def task1():
    time.sleep(2)
    print("in the task1")

@timer(parameter='task2')
def task2():
    time.sleep(2)
    print("in the task2")

task1()
task2()

至此,装饰器的全体内容截至。

壹.1个函数的函数名作为另贰个函数的参数

import time
def bar():
    time.sleep(2)
    print("in the bar")

def test1(func):
    start_time=time.time()
    func()
    stop_time=time.time()
    print("the test run time is %s"%(stop_time-start_time))


bar()
print("-----------")
test1(bar)

必发88手机版 5

通过上面的事必躬亲是或不是近水楼台先得月五个定论:高阶函数能够在不转移原函数的源代码的动静下,为其扩张效益,但会转移原函数的调用格局

二.二个函数的再次来到值包蕴函数名

import time
def bar():
    time.sleep(2)
    print("in the bar")


def test1(func):
    start_time=time.time()
    func()
    stop_time=time.time()
    print("the test run time is %s" % (stop_time - start_time))
    return func


bar=test1(bar)
print("------")
bar()

必发88手机版 6

经过地点的亲自去做是或不是近水楼台先得月2个结论:高阶函数能够不转移原函数的调用方式,然则并没有增多新成效

c.嵌套函数

def foo():
    print("in the foo")
    def bar():
        print("in the bar")
    bar()

foo()

必发88手机版 7

5.写装饰器

a.装饰器是为给任何函数扩展效果,可是不能够改换原函数的源代码,那我们怎么技艺不改换原函数的源代码,回想上文,通过高阶函数(将函数名作为另一个函数的参数)定义3个新函数,在新函数中调用原函数,并且加多新功用,那样就可以兑现大家的对象

#现在我有一个函数tets1,然后为它添加一个新功能
def dec(func):
    print("我是新功能!")
    func()

def test1():
    print("in the test1")

test1()
print("-------")
dec(test1)

必发88手机版 8

b.在不更动原函数的还要,而且也无法改改函数的调用格局,函数的调用形式是否函数名(),那么函数即“变量”,在此处就起大职能了,在地点代码中,大家能够将新函数的函数地址赋给原函数的函数名,原函数的函数名对应的内部存款和储蓄器空间产生了转移,那片新的内部存款和储蓄器空间中既有原函数的机能,也可能有新增的效率,那么大家通过高阶函数(重回值中带有函数名)将原函数的函数名重新赋值,那样大家的指标就高达了。

#因为需要返回新函数的函数名(函数地址),所以要使用嵌套函数
def foo(func):             #func=test1  将test1函数地址传给了func   所以新函数的内存空间保存了原函数的内存地址,可以通过调用新函数来调用原函数,并且增加功能
    def dec():
        print("我是新功能!")
        func()
    return dec

@foo    #相当于test1=foo(test1)    在python中 装饰器 用@装饰圈名 来表示
def test1():
    print("in the test1")

test1()
print("-------")
#test1=foo(test1)
test1()

必发88手机版 9

陆.高级版装饰器

import time
def timer(func):            #func=test1   将test1的地址赋给func
    def deco(*args,**kwargs):
        start_time = time.time()
        res=func(*args,**kwargs)              #相当于调用test1
        stop_time=time.time()
        print("the func run time is %s" % (stop_time - start_time))
     return res
    return deco            #将deco的地址返回

@timer               #相当于test1=timer(test1)
def test1():
    time.sleep(2)
    print("in the test1")
@timer
def test2(name):
    time.sleep(2)
    print("in the test2",name)
    return name

# test1=timer(test1)            #test1=deco 将deco的地址赋给test1
test1()                       #相当于调用deco
test2("phk")

必发88手机版 10

以上代码中,装饰器的效果是为八个函数扩展计时功能,与事先有所不相同的是,原函数中是带参数和再次回到值,那么大家得以运用*args **kwargs来充当函数参数,与参数同样,大家给新函数定义重临值,那样就完毕了透明化管理.

7.高潮版

user,passwd="phk","123"

def auth(auth_type):
    #print(auth_type)
    def outer(func):
        #print(func)
        def deco(*args,**kwargs):
            if auth_type == "local":
                username=input("usernane:").strip()
                password=input("password:").strip()
                if user==username and passwd==password:
                    func()
                else:
                    exit("账号密码错误")
            elif auth_type=="ldap":
                print("不会")
        return deco
    return outer

def index():
    print("welcome to index page")

@auth(auth_type="local")            #相当于home=auth(auyh_type="local")(func)
def home():
    print("welcome to home page")

@auth(auth_type="ldap")
def bbs():
    print("welcome to bbs page")

index()
home()
bbs()

装饰器的职能是为四个页面函数增加登60%效,区别的是索要采取本地登录还是ldap登录,说白了就是在装饰器外面在套壹层函数,传进来的参数能够当做局地参数,能够咱函数体内使用,然而急需掌握有些

 @auth(auth_type="local") 相当于home=auth(auyh_type="local")(func)。

 解释器会先翻译“auth(auth_type = 'local')”,再将其再次回到值(借使给了_func那么些不设有的函数名)当作函数指针,这里的_func函数名代表的是outer,然后再去执行“func = _func(func)”,而以此func函数名代表的实际上就是deco。

 

本文由必发88手机版发布,转载请注明来源:Python装饰器的易懂明了