fudonghai

导航

 

一,什么是装饰器

本质就是函数,功能是为其他函数添加附加功能

原则:

1,不修改被修饰函数的源代码

2,不修改被修饰函数的调用方式

例子:

import time

def timmer(func):
    def wapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print('函数的运行时间 %s' % (stop_time - start_time))
        return res
    return  wapper

@timmer      #加装饰器
def cal(l):  #原函数
    res = 0
    time.sleep(1)
    for i in l:
        res += i
    return res

t1 = cal(range(100))
print(t1)
'''输出:
函数的运行时间 1.0000572204589844
4950
'''

 

二,装饰器的知识储备

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

(一)高阶函数

定义:

1,函数接受的参数是一个函数名

2,函数的返回值是一个函数名

3,满足上述条件任意一个,都可称之为高阶函数

例子:高阶函数

def foo():
    print('你好啊林师傅')

def test(func):
    print(func) #打印内存地址
    func()      #执行函数foo

test(foo) #test是高阶
'''输出:
<function foo at 0x00000000022F22F0>
你好啊林师傅
'''

例子:函数接受的参数是一个函数名,就能实现给函数增加功能的目标

def foo():
    time.sleep(3)
    print('你好啊林师傅')

def test(func):
    start_time = time.time()
    func()      #执行函数foo
    stop_time = time.time()
    print('函数的运行时间 %s' % (stop_time - start_time))

test(foo) #修改了函数原来的调用方式,原来是foo()
'''输出:
你好啊林师傅
函数的运行时间 3.000171661376953
'''

例子:如果再加上返回值是一个函数,则能实现“不修改被修饰函数的调用方式”这一个条件

def foo():
    print('from the foo')
def test(func):
    return func

# res = test(foo)
# print(res) #res得到的是foo的内存地址
# res()      #实际是运行foo
foo = test(foo) #如果把函数的执行结果重新赋值给foo函数名
foo()           #就能实现“不修改被修饰函数的调用方式”这一个条件
'''输出:
from the foo
'''

例子:多运行一次,并不合格(高阶函数并不能全部满足装饰器的需要)                1,不修改foo源代码,2,不修改foo调用方式

def foo():
    time.sleep(3)
    print('from the foo')

def timer(func):
    start_time = time.time()
    func()      #执行函数foo
    stop_time = time.time()
    print('函数的运行时间 %s' % (stop_time - start_time))
    return func

foo = timer(foo) 
foo()           #又执行了一次函数foo  
'''输出:
from the foo
函数的运行时间 3.007171869277954
from the foo
'''

 (二),函数嵌套

定义:在函数里面定义另一个函数

def bar():
    print('from bar')

def foo():
    print('from foo')
    def test():
        pass

函数闭包:

闭——封装变量,包——层层嵌套。其实和函数作用域是一回事。特性:如果内层没有定义重名变量,外层的变量可以传进来。

def father(auth_type):
    # print('from father %s' %name)
    def son():
        # name='linhaifeng_1'
        # print('我的爸爸是%s' %name)
        def grandson():
            print('我的爷爷是%s' %auth_type)#实参'filedb'从最外层一直传导进来
        grandson()
    # print(locals())
    son()
# father('linhaifeng')
father('filedb')

#输出:我的爷爷是filedb

 

三,装饰器框架

使用高阶函数+函数闭包完成装饰器框架。

个人理解:1,装饰器函数传入的原函数名func,用在最内层运行原函数,以实现和原函数一样的效果

                  2,而在原函数外层包裹的一层函数wrapper,加retunrn wapper返回的是加入新功能后的一个函数地址,在外面运行这个函数地址也就执行了加入的新功能

                  3,而@这样一个语法把新函数的地址赋值给原函数名,这样运行原函数名,就把新的功能增加了。         

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()
'''输出:
test函数运行完毕
运行时间是3.000171661376953
'''

#先使用res来接收
# res=timmer(test)  #返回的是wrapper的地址
# res()  #执行的是wrapper()

#然后使用test原函数名替换res
# test=timmer(test)  #返回的是wrapper的地址
# test()  #执行的是wrapper()

#最后python提供@语法等效于test=timmer(test)
#  @timmer  就相当于 test=timmer(test)

 例子:原函数加入返回值情况

import time
def timmer(func):
    def wrapper():
        start_time=time.time()
        res=func()           #使用res接收返回值
        stop_time = time.time()
        print('运行时间是%s' %(stop_time-start_time))
        return res           #把返回值传回去
    return wrapper

@timmer
def test():
    time.sleep(3)
    print('test函数运行完毕')
    return '这是test的返回值'  #这里原函数test加入了返回值

res=test()
print(res)
'''输出:
test函数运行完毕
运行时间是3.007171869277954
这是test的返回值
'''

例子:加入参数,并且考虑原函数参数不固定的情况

#传参的复习和回顾
def test2(name,age,gender): #test2(*('alex',18,'male','x','y'),**{})
    #name,age,gender=('alex',18,'male','x','y')
    print(name)
    print(age)
    print(gender)

def test1(*args,**kwargs):
    test2(*args,**kwargs)  #args=('alex',18,'male','x','y') kwargs={}

test1('alex',18,gender='male') #正确的传参方式
test1('alex',18,'male')        #正确的传参方式
test1('alex',18,'male','x','y')#多传了两个参数,报错:test2() takes 3 positional arguments but 5 were given
#(*args,**kwargs)实现了传递可变参数的功能,在原函数有多个变体的情况下,装饰器函数可以通用
import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs) #可以理解为wrapper得到的参数原封不动的传进原函数
        print('args 是 %s:' %(args,))
        print('kwargs 是: %s' %kwargs)
        stop_time = time.time()
        print('运行时间是%s' %(stop_time-start_time))
        return res
    return wrapper

@timmer
def test(name,age):         #两个参数
    time.sleep(1)
    print('test函数运行完毕,名字是【%s】 年龄是【%s】' %(name,age))
    return '这是test的返回值'

@timmer
def test1(name,age,gender): #三个参数
    time.sleep(1)
    print('test1函数运行完毕,名字是【%s】 年龄是【%s】 性别【%s】' %(name,age,gender))
    return '这是test1的返回值'

test('linhaifeng',age=18) #args=('linhaifeng')     kwargs={'age':18}
print('######')
test1('alex',18,'male')   #args=('alex',18,'male') kwargs={}

'''输出: test函数运行完毕,名字是【linhaifeng】 年龄是【18】 args 是 ('linhaifeng',): kwargs 是: {'age': 18} 运行时间是1.0000572204589844 ###### test1函数运行完毕,名字是【alex】 年龄是【18】 性别【male】 args 是 ('alex', 18, 'male'): kwargs 是: {} 运行时间是1.0000572204589844 '''

两个小技巧

#1,解压序列
a,*_,c =[10,9,8,7,6]#代表所有值
print(a)
print(_) #下划线_是变量
print(c)
'''输出:
10
[9, 8, 7]
6
'''

#2,交换两个变量值 f1 = 1 f2 = 10 f1,f2 = f2,f1 print(f1,f2) #输出:10 1

 

四,装饰器实战

(一)使用装饰器实现验证和session功能

user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]
current_dic={'username':None,'login':False}


def auth_func(func):
    def wrapper(*args,**kwargs):
        if current_dic['username'] and current_dic['login']:
            res = func(*args, **kwargs)
            return res
        username=input('用户名:').strip()
        passwd=input('密码:').strip()
        for user_dic in user_list:
            if username == user_dic['name'] and passwd == user_dic['passwd']:
                current_dic['username']=username
                current_dic['login']=True
                res = func(*args, **kwargs)
                return res
        else:    #######这个else非常有价值,不是if的else,而是for的else
            print('用户名或者密码错误')

    return wrapper

@auth_func
def index():
    print('欢迎来到京东主页')

@auth_func
def home(name):
    print('欢迎回家%s' %name)

@auth_func
def shopping_car(name):
    print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃'))

print('before-->',current_dic)
index()
print('after--->',current_dic)
home('产品经理')
# shopping_car('产品经理')

 

五,装饰器运行流程

1,运行@装饰器函数名,实际动作是运行装饰器函数,由于闭包函数wrapper定义在这个装饰器函数的里面,所以仅return wrapper,把wrapper的函数地址返回给原函数名(重新赋值)

2,当程序运行到原函数名(),也就是执行原函数时,因为前面的步骤,实际跳转到wrapper函数执行。这里面巧妙的地方是,wrapper函数里面又包含一个原函数(),这回是真正执行原函数()。

3,执行完原函数后,回到wrapper里面,原函数()下面的步骤运行。

 

六,带参数的装饰器函数

本质就是在外面再添加一层,把原装饰器函数包裹起来。原装饰器函数实现的是把原函数名传递进去在内层,带参数的装饰器函数实现传递功能性参数作用在最外层,层层嵌套。

user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'linhaifeng','passwd':'123'},
    {'name':'wupeiqi','passwd':'123'},
    {'name':'yuanhao','passwd':'123'},
]
current_dic={'username':None,'login':False}

def auth(auth_type='filedb'):
    def auth_func(func):
        def wrapper(*args,**kwargs):
            print('认证类型是',auth_type)
            if auth_type == 'filedb':
                if current_dic['username'] and current_dic['login']:
                    res = func(*args, **kwargs)
                    return res
                username=input('用户名:').strip()
                passwd=input('密码:').strip()
                for user_dic in user_list:
                    if username == user_dic['name'] and passwd == user_dic['passwd']:
                        current_dic['username']=username
                        current_dic['login']=True
                        res = func(*args, **kwargs)
                        return res
                else:
                    print('用户名或者密码错误')
            elif auth_type == 'ldap':
                print('鬼才特么会玩')
                res = func(*args, **kwargs)
                return res
            else:
                print('鬼才知道你用的什么认证方式')
                res = func(*args, **kwargs)
                return res

        return wrapper
    return auth_func

@auth(auth_type='filedb') #auth_func=auth(auth_type='filedb')-->@auth_func 附加了一个auth_type  --->index=auth_func(index)
def index():
    print('欢迎来到京东主页')

@auth(auth_type='ldap')
def home(name):
    print('欢迎回家%s' %name)
#
@auth(auth_type='sssssss')
def shopping_car(name):
    print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃'))

# print('before-->',current_dic)
# index()
# print('after--->',current_dic)
# home('产品经理')
shopping_car('产品经理')

 

posted on 2019-01-26 15:36  fudonghai  阅读(194)  评论(0编辑  收藏  举报