python之函数装饰器详解

装饰器


定义:本质是函数,(装饰其他函数),就是为其他函数添加附加功能。

原则:

  1. 不能修改被装饰的函数的源代码

  2. 不能修改被装饰的函数的调用方式

  3. 不能影响原函数的返回值

公式:高阶函数 + 嵌套函数 + 运用函数即变量的思想 = 装饰器

示例:

#装饰器,为函数test加上计时功能
def timer(func):
    def secend(*name):
        start_time = time.time();
        res = func(*name)
        end_time = time.time();
        print("运行时间为%s秒"%(end_time - start_time))
        return res
    return secend

@timer  
def test():
    time.sleep(1)
    print("this is test func")

-----------------------------------------------------------------------分割线---------------------------------------------------------------------------

在搞清楚装饰器之前,我们需要理解3个东西:

1、函数即“变量”:

函数(名)可以相当于一个变量,可以进行赋值等等操作,实际上函数名只是函数内容的存放地址,可以把它等价为 x = y 这样赋值(假设x,y均为函数名)。

2、高阶函数:

    高阶函数形式有两种:

(1)把一个函数名当作实参传给另一个函数(为不修改被装饰函数源代码的情况下为其添加功能做准备)。

(2)返回值是一个函数名或者返回值中包含函数名(为不修改函数的调用方式做准备)。

3、嵌套函数

就是函数内部又有一个函数,比如下面:

#嵌套函数
def a():
    print('a')
    def b():
        print('b')
        print('bbb')
    b() #调用b函数
a() #调用a函数

了解上面三个知识点后。

记住一个公式:高阶函数 + 嵌套函数 + 运用函数即变量的思想 = 装饰器


现在写一个装饰器,用来计算那个函数要花的时间。

注意:1、装饰器要写在原函数前面,所以先看test原函数,再看timer装饰器。

2、始终理解:高阶函数 + 嵌套函数 + 运用函数即变量的思想 = 装饰器。

import time

'''装饰器,计算test花费时间'''
def timer(func):    #函数可以作为参数传进另一个函数(高阶函数),第一层传入一个函数
    def secend():   #第二层函数加入新的功能
        start_time = time.time();
        res = func()  #调用原来的函数(func()中:func代表传入的那个函数,右边写上一对小括号等于调用的意思),返回值用res接收
        end_time = time.time();
        print("运行时间为%s秒"%(end_time - start_time))   #打印耗时
        return res  #这里用res接收func的返回值并return,确保不遗漏返回值
    return secend

'''原函数'''
# @timer  #等价于这句话:test = timer(test),也就是test= secend,现在test() = secend()
def test():
    time.sleep(1)
    print("this is test func")
    return 1

'''
timer(test)的返回值是secend,(不带括号),也就是新写出来的函数的内容的地址,包括了新的功能,赋值给原来的test替换掉
(函数即变量),在运行原来的test,就可以使用到装饰器的功能,并且调用test()的地方不用删除,只需要增加一句话,也不改变原
来函数里面的内容。但因为这样改动麻烦(要在所有调用过test的地方改),所以python给了一种方法:在test函数头加上一句:@timer,
在下面会介绍到。
'''
test = timer(test)  
test()

运行结果为:(没有改变原来的功能和调用方式)

 


代码里的注释都有详细的解释,可自行查看。

直接在要加装饰器的函数头加上{@装饰器}是一样的效果。这样省去了在每次调用的地方改函数

因为要让大家理解一下函数的赋值问题,所以上面用了test = timer(test)这句代码。

完成上面代码之后,我们会发现,当我们要去装饰的函数有的有参数,有的没有参数的时候,这个时候会出现错误:

因为装饰器不能吧“LIKUNKUN ”这个字符串参数接收到。

之前说到,调用testWithParameter就等于调用secend(),所以只需要在secend(加入参数)就可以了,改动这一部分代码:

#装饰器
def timer(func):
    def secend(name):
        start_time = time.time();
        res = func(name)
        end_time = time.time();
        print("运行时间为%s秒"%(end_time - start_time))
        return res
    return secend

但这样的话,之前的test代码又运行不了了,因为test并没有参数,于是python提供给参数前面加上*,表示可选参数

改动之后不管一个参数,两个参数,没有参数,都可以运行了:

#装饰器
def timer(func):
    def secend(*args,**kwargs):
        start_time = time.time();
        res = func(*args,**kwargs)
        end_time = time.time();
        print("运行时间为%s秒"%(end_time - start_time))
        return res
    return secend

@timer  
def test():
    time.sleep(1)
    print("this is test func")

@timer  #testWithParameter = timer(testWithParameter)
def testWithParameter(name):
    time.sleep(1)
    print("这是有参数的函数,参数为:",name)

test()
testWithParameter('LIKUNKUN')

到此,已经可以解决90%以上的装饰器的应用了。


下面是更进一步的用法:给装饰器传入一个参数

根据之前的内容,可以写出下面的代码:

3个函数,代表3个界面(仅用print表示),第一个是索引界面,不需要登录,后面是主界面和属性界面,需要登录验证:

user = 'li'
passsd = '123'

def sure(func):
    def wrapper(*args,**kwargs):
        username = input("用户名:").strip() #strip用来去除首位空格和换行符之类的无效符号
        password = input("密码:").strip()

        if user == username and passsd ==password:
            return func(*args,**kwargs) #这里的return是当func表示的函数有返回值的时候,返回同样的值
        else:
            exit('账号密码错误')
    return wrapper

#索引直接进入,后面的主页面和属性界面需要验证
def index():
    print('到了索引界面')

@sure
def home():
    print('到了主界面')

@sure
def power():
    print('到了属性界面')

index()
home()
power()

运行结果:

现在我需要加入一个功能:进入主界面进行的是本地验证,进入属性界面进行的是网络验证,也就是需要多种验证模式。

但现在的装饰器只能支持本地验证,这时候就需要在装饰器上传入一个参数,如图:

怎么实现呢?再嵌套一层函数就好了,看下面代码,代码里注释有详解:

user = 'li'
passsd = '123'

#在套一层函数就好了
def sure(sureType): #这里传入的值就是装饰器传进来的
    def outer(func):    #之前的func传到这里来了
        def wrapper(*args, **kwargs):
            if sureType == 'local': #到这里在修改判断本地和网络的代码
                username = input("用户名:").strip()
                password = input("密码:").strip()

                if user == username and passsd == password:
                    return func(*args, **kwargs)
                else:
                    exit('账号密码错误')
            elif sureType == 'web':
                print('网络验证不会写,直接通过')
                return func(*args, **kwargs)
            else:
                exit('还没开发的验证类别,直接退出')
        return wrapper
    return outer

#索引直接进入,后面的主页面和属性界面需要验证
def index():
    print('到了索引界面')

@sure(sureType = 'local')
def home():
    print('到了主界面')
    return 'home'

@sure(sureType = 'web')
def power():
    print('到了属性界面')

index()
home()
power()

运行结果:

至此,python之装饰器的内容,就结束了。

猜你喜欢

转载自blog.csdn.net/likunkun__/article/details/81570469