首页 > 技术文章 > Python3 装饰器

lvcm 2018-09-13 09:47 原文

一 什么是装饰器呢?

装饰器本质上来说就是函数,功能就是为其它函数添加附加功能。 

原则:

  1. 不修改被修饰函数的源代码
  2. 不修改被修饰函数的调用方式

组织结构:

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

实例1(Python3.0+):

# 使用高阶函数试图完成装饰器
import time
                
# 定义函数foo
def foo():
    time.sleep(3)
    print('你好啊,林师傅')

# 定义函数test来装饰foo函数,计算foo函数的运行时间
def test(func):
    start_time = time.time()
    print(func)
    func()
    stop_time=time.time()
    print('函数的运行时间:%s' %(stop_time-start_time))

test(foo)
# 改变了被修饰函数(foo())的调用方式,不满足装饰器的特性,所以不是合格的装饰器

实例2(Python3.0+):

简单装饰器的创建思路

import time
# 先架设装饰器的架子:高阶函数+函数嵌套+闭包
def timer(func): # func=test
    def wrapper():
        start_time=time.time()
        # print(func)
        func() # 就是在运行test()
        stop_time=time.time()
        print('函数的运行时间:%s' %(stop_time-start_time))
    return wrapper

def test():
    time.sleep(2)
    print('函数运行结果')

# test返回的就是wrapper
test=timer(test)   
# 执行test()就相当于执行wrapper()
test()
'''
以上例子实现了装饰器的部分功能: 
1、没有改变原有函数的源代码,但是改变了函数的调用方式
'''

# 思考:怎么才能不改变被修饰函数的调用方式呢,我们可以采用语法糖的方式。
# 语法糖  @
# @timer 就相当于test=timer(test)

import time
# 架设装饰器的架子:高阶函数+函数嵌套+闭包
def timer(func): # func=test
    def wrapper():
        start_time=time.time()
        # print(func)
        func() # 就是在运行test()
        stop_time=time.time()
        print('函数的运行时间:%s' %(stop_time-start_time))
    return wrapper

@timer
def test():
    time.sleep(2)
    print('函数运行结果')

test()

# 如果我想打印被修饰函数的返回值呢?:
# 实例1:
#
import time
# 装饰器的架子:高阶函数+函数嵌套+闭包
def timer(func): # func=test
    def wrapper():
        start_time=time.time()
        # print(func)
        func() # 就是在运行test()
        stop_time=time.time()
        print('函数的运行时间:%s' %(stop_time-start_time))
    return wrapper

@timer # test = timer(test)
def test():
    time.sleep(2)
    print('函数运行结束')
    return '这是test的返回值'

res=test()
# test()实际上运行的是wrapper() 原因:1.@timer 等于 test=timer(test) ,所以实际上运行test()相当于timer()(),也就是test=wrapper
print(res)
# 实际上打印的是wrapper的返回值,但wrapper函数没有ruturn返回值,所以得到的结果为None

'''
函数运行结束
函数的运行时间:2.0001144409179688
None
'''

# 实例1中没有实现打印被修饰函数返回值的功能,看看以下代码呢;
# 实例2:
# 
import time
# 装饰器的架子:高阶函数+函数嵌套+闭包
def timer(func): # func=test
    def wrapper():
        start_time=time.time()
        res=func()
        # print(func)
        # func() # 就是在运行test()
        stop_time=time.time()
        print('函数的运行时间:%s' %(stop_time-start_time))
        return res
    return wrapper

@timer # test = timer(test)
def test():
    time.sleep(2)
    print('函数运行结束')
    return '这是test的返回值'

print(test())
'''
打印结果:
函数运行结束
函数的运行时间:2.0001142024993896
这是test的返回值
'''  

实例3(Python3.0+):

实例2中虽然已经实现了装饰器的功能,但是被修饰的函数是没有形参的,如果我们在被修饰的函数中定义形参呢,那实例2中的装饰器就不能完成这个任务了吧??

# 带参数的装饰器
# 
import time
# 装饰器的架子:高阶函数+函数嵌套+闭包
def timer(func): # func=test
    def wrapper(*args,**kwargs):  # 相当于定义函数 def test(*args,**wargs):
        start_time=time.time()
        res=func(*args,**kwargs) # 就是在运行test()
        # print(func)
        # func() # 就是在运行test()
        stop_time=time.time()
        print('函数的运行时间:%s' %(stop_time-start_time))
        return res
    return wrapper  # test=wrapper

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

# test()
print(test('lvcm',19))

'''
运行结果:
函数运行结束
test函数运行完毕,名字是lvcm 年龄是19
函数的运行时间:2.0001144409179688
这是test的返回值
'''

二 练习题:

1、模仿京东商城登录

# 简单的京东登录架构
# 进入京东主页
def index():
    print('欢迎来到京东主页')

# 进入个人主页
def home(name):
    print('欢迎回家%s' %name)

# 打印购物车物品
def shopping_car(name):
    print('%s购物车里有[%s,%s,%s]'%(name,'丝袜','娃娃','奶茶'))

# 打印个人订单
def order():
    print('您的订单是.......')

index()
home('lvcm')
shopping_car('lvcm')
order()

# 需求:给每个函数加上一个用户登录的验证功能
# 
# 定义用户默认登录状态
dict_user={'username':None,'login':False}

# 定义用户登录验证装饰器
def auth_login(func):
    def wrapper(*args,**kwargs):
        # print('您未登录,请先登录,谢谢...')

        while True:
            if  dict_user['username']=='lvcm' and dict_user['login'] == True:
                res=func(*args,**kwargs)
                return res
            user_name=input('请输入用户名:').strip()
            user_passwd=input('请输入密码:').strip()
            if  user_name=='lvcm' and user_passwd=='123':
                print('%s登录成功' %user_name)
                dict_user['username']=user_name
                dict_user['login']=True
                break
            else:
                print("用户名密码错误,请重新输入")
        res=func(*args,**kwargs)
        return res
    return wrapper

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

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

@auth_login
def shopping_car(name):
    print('%s购物车里有[%s,%s,%s]'%(name,'丝袜','娃娃','奶茶'))

@auth_login
def order():
    print('您的订单是.......')

index()
'''
运行结果:
请输入用户名:lvcm
请输入密码:123
lvcm登录成功
欢迎来到京东主页
'''
home('lvcm')
'''
运行结果:
请输入用户名:lvcm
请输入密码:123
lvcm登录成功
欢迎回家lvcm
'''
shopping_car('lvcm')
'''
运行结果:
请输入用户名:lvcm
请输入密码:123
lvcm登录成功
lvcm购物车里有[丝袜,娃娃,奶茶]
'''
order()
'''
运行结果:
请输入用户名:lvcm
请输入密码:123
lvcm登录成功
您的订单是.......
'''  

上述例题中我们模仿了京东个人登录验证功能,但是代码中发现一个很有意思的问题,就是没次刷新页面时都要验证登录,这是很不合理的,其实登录验证一次就可以了。

2、复杂的仿京东程序

# 定义用户列表
user_list=[
    {'name':'lvzf','passwd':'123'},
    {'name':'zhangjm','passwd':'123'},
    {'name':'lvcm','passwd':'123'},
    {'name':'muyb','passwd':'123'},
]

# 定义用户登录初始状态
current_dict={'username':None,'login':False}

def auth_login(func):
    def wrapper(*args,**kwargs):
        # print('您未登录,请先登录,谢谢...')

        while True:
            if  current_dict['username'] and current_dict['login']:
                res=func(*args,**kwargs)
                return res
            username=input('请输入用户名:').strip()
            userpasswd=input('请输入密码:').strip()
            for user_dict in user_list:
                if  username==user_dict['name'] and userpasswd==user_dict['passwd']:
                    print('%s登录成功' %username)
                    current_dict['username']=username
                    current_dict['login']=True
                    break
            else:
                print("用户名密码错误,请重新输入")
            res=func(*args,**kwargs)
            return res
    return wrapper


print('before is ')

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

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

@auth_login
def shopping_car(name):
    print('%s购物车里有[%s,%s,%s]'%(name,'丝袜','娃娃','奶茶'))

@auth_login
def order():
    print('您的订单是.......')

index()
home('lvcm')
shopping_car('lvcm')
order()

推荐阅读