Python的装饰器

     装饰器

       初识装饰器,有一种高大上的感觉,但又不知道是啥。从字面意义上来看:装饰,就是起修饰作用,附加功能;器,就是函数;既然是函数,那就好说了,我先写这么一个函数。

    装饰器的两个原则:

      1.不改变原函数

      2.不改变原函数的调用方式

       怎么理解这两句话?不改变原函数,就是不动函数的代码,就给他添加一个功能;不改变原函数的调用方式也是一样。

       装饰器 = 高阶函数+函数嵌套+闭包

       这是装饰器的组成,如何一步步来实现装饰器,先看高阶函数行不行,现在我要给一个函数添加一个打印运行时间的功能,我先这样写:

import time

def foo():
    time.sleep(2)
    print('this is my house')

start_time = time.time()
foo()
stop_time = time.time()
total_time  = stop_time - start_time
print ("一共用了 %s 秒"%total_time)

      这样已经实现了想要的功能,把下面这一段写成高阶函数,似乎可以大功告成

import time

def foo():
    time.sleep(2)
    print('this is my house')

def timer(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    total_time  = stop_time - start_time
    print ("一共用了 %s 秒"%total_time)
    return func

foo = timer(foo)
foo()

       先介绍一个高阶函数的定义,就是函数接收的参数是一个函数名或函数返回的参数是一个函数名,满足其中一个条件就是高阶函数。上面的运行结果是:

>>> this is my house
>>> 一共用了 2.000192165374756 秒
>>> this is my house

     为了不影响原函数调用,我将高阶函数也赋值给foo,但是最后运行了两次,并不满足要求,看来单用高阶函数不能满足要求,配合函数嵌套和闭包试一下。先看看函数嵌套的例子:

def father(name):
    print ('my father is %s' %name)
    def son():
        print('my son is %s' %name)  #当前函数没有name时,就往上一层找,这叫函数的作用域
        def grandson():
            name = '6788'
            print('我的爷爷是%s' %name)
        grandson()
    son()

father('23455')

      这个例子演示了函数嵌套和闭包,闭包是哪个?闭包就是里面的嵌套函数。上面用高阶函数,会多运行一次,因为在foo = timer(foo)已经运行一次了,函数嵌套好像可以处理这个问题,试一下:

import time
def timmer(func): #func=test
    def wrapper():
        # print(func)
        start_time=time.time()
        func() #就是在运行test()
        stop_time = time.time()
        print('运行时间是%s' %(stop_time-start_time))
    return wrapper

def test():
    time.sleep(3)
    print('test函数运行完毕')

test=timmer(test)  #返回的是wrapper的地址
test()  #执行的是wrapper()

       看一下运行结果:

>>> test函数运行完毕
>>> 运行时间是3.0001602172851562

     我去,成功了,一个简单的装饰器就这么实现了。在Python中,装饰器有自己的写法(@装饰器的函数名),就是这样的:

import time
def timmer(func): #func=test
    def wrapper():
        # print(func)
        start_time=time.time()
        func() #就是在运行test()
        stop_time = time.time()
        print('运行时间是%s' %(stop_time-start_time))
    return wrapper

@timmer #test=timmer(test)
def test():
    time.sleep(3)
    print('test函数运行完毕')

test()

        其中@timmer就是test= timmer(test)嘛,也满足装饰器的两个原则,完美!

        但是上面的test函数式最简单的函数,如果函数有返回值呢,如果带参数呢?是不是还可以这么写?现在试一下带返回值的:

import time
def timmer(func): #func=test
    def wrapper():
        # print(func)
        start_time=time.time()
        func() #就是在运行test()
        stop_time = time.time()
        print('运行时间是%s' %(stop_time-start_time))
    return wrapper

@timmer #test=timmer(test)
def test():
    time.sleep(3)
    print('test函数运行完毕')
    return '返回值'

print(test())

       看一下结果:

>>> test函数运行完毕
>>> 运行时间是3.005080461502075
>>> None

        怎么会是None?明明有返回值啊。看一下怎么运行的,print里面test()已经运行了,在wrapper()里面并没有返回值,难怪会出现None,那我加上一个就好了:

import time
def timmer(func): #func=test
    def wrapper():
        # print(func)
        start_time=time.time()
        ret = func() #就是在运行test()
        stop_time = time.time()
        print('运行时间是%s' %(stop_time-start_time))
        return ret
    return wrapper

@timmer #test=timmer(test)
def test():
    time.sleep(3)
    print('test函数运行完毕')
    return ('test的返回值')

print(test())

        这就OK了嘛。返回值搞定了,参数肯定更简单,我试一下加参数的:

import time
def timmer(func): #func=test
    def wrapper():
        # print(func)
        start_time=time.time()
        ret = func() #就是在运行test()
        stop_time = time.time()
        print('运行时间是%s' %(stop_time-start_time))
        return ret
    return wrapper

@timmer #test=timmer(test)
def test(name,age):
    time.sleep(3)
    print('test函数运行完毕,名字是%s,年龄是%s' %(name,age))
    return ('test的返回值')

ret = test('dfg',12)
print(ret)

       看一下结果:

TypeError: wrapper() takes 0 positional arguments but 2 were given

       报错了,这可不得报错嘛,参数都不给,现在给wrapper()和func()加上参数试一下:

import time
def timmer(func): #func=test
    def wrapper(x,y):
        # print(func)
        start_time=time.time()
        ret = func(x,y) #就是在运行test()
        stop_time = time.time()
        print('运行时间是%s' %(stop_time-start_time))
        return ret
    return wrapper

@timmer #test=timmer(test)
def test(name,age):
    time.sleep(3)
    print('test函数运行完毕,名字是%s,年龄是%s' %(name,age))
    return ('test的返回值')

ret = test('dfg',12)
print(ret)

       运行一下:

test函数运行完毕,名字是dfg,年龄是12
运行时间是3.0100483894348145
test的返回值

      没问题,很完美。但是想一下,函数都是两个参数的吗?参数有多少种?对啊,用*args和**kwargs就搞定一切啊,走起:

import time
def timmer(func): #func=test
    def wrapper(*args,**kwargs):
        # print(func)
        start_time=time.time()
        ret = func(*args,**kwargs) #就是在运行test()
        stop_time = time.time()
        print('运行时间是%s' %(stop_time-start_time))
        return ret
    return wrapper

@timmer #test=timmer(test)
def test(name,age):
    time.sleep(3)
    print('test函数运行完毕,名字是%s,年龄是%s' %(name,age))
    return ('test的返回值')

@timmer #test=timmer(test)
def test1(name,age,wifi):
    time.sleep(3)
    print('test函数运行完毕,名字是%s,年龄是%s,wifi是%s' %(name,age,wifi))

ret = test('dfg',12)
print(ret)
test1('dfaaa',12,'765hsdf')

         这样就基本搞定了装饰器了,很简单嘛。但是有个问题,装饰器可以带参数吗?为什么不行?都是函数啊,试一下,做一个模拟淘宝登录流程,添加认证功能:

import time

auth_list = [
    {'username':'444','passwd':'123'},
    {'username':'555','passwd':'123'},
    {'username':'666','passwd':'123456'},
    {'username':'777','passwd':'123456'}
]
wo_dic = {'username':None,'login':False}

def check(auth_type='filedb'):
    def check_01(func):

            def wrecur(*args,**kwargs):
                if auth_type == 'filedb':
                    if wo_dic['username'] and wo_dic['login']:
                        ret = func(*args, **kwargs)
                        return ret

                    username = input('用户名:').strip()
                    passwd = input('密码:').strip()
                    for i in auth_list:
                        if username == i['username'] and passwd == i['passwd']:
                            wo_dic['username'] = username
                            wo_dic['login'] = True
                            ret = func(*args, **kwargs)
                            return ret
                    else:
                        print('用户名或密码错误')
                elif auth_type == 'otherdb':
                    print('鬼都不知道怎么用')
                else:
                    print('写的什么鬼')
            return wrecur
    return check_01


@check(auth_type='filedb')
def index():
    print('欢迎来到淘宝主页')

@check(auth_type='otherdb')
def home(name):
    print('%s,欢迎回家' %name)

@check(auth_type='ssssss')
def shopping_car():
    print('购物车里有:%s,%s,%s' %('香蕉','牛奶','山羊'))

index()
home('产品经理')
shopping_car('产品经理')

      这样就实现了不同参数的装饰器对应不同的认证方式,装饰器基本就这些知识点了。

    

原文地址:https://www.cnblogs.com/pengfy/p/10560410.html