python中装饰器介绍

装饰器:

  • 把一个函数当作参数,返回一个替代版的函数

  • 本质上就是一个返回函数的函数

  • 在不改变原函数的基础上,给函数增加功能

import time
def decorator(func):   ##wrapper里不写东西不能接收参数
    def wrapper():
        print(time.time())   ##打印系统运行的时间
        func()     ##调用传进来的函数,也就是f1()——>打印:This is a function
    return wrapper   ##如果不返回,则不会执行这个wrapper函数


@decorator
def f1():
    print('This is a function')

f1()
结果:
1547992084.6096034
This is a function
##wrapper里想要接收参数
import time

def decorator(func):
    def wrapper(*args,**kwargs):  ##表示接收任意多个参数,也可以接收字典
        print(time.time())
        func(*args,**kwargs)  ##此处接收的参数与wrapper一致
    return wrapper

@decorator
def f3(func_name1,func_name2,**kwargs):
    print('This is function ' + func_name1)
    print('This is function ' + func_name2)
    print(kwargs)

f3('test1','test2',a=1,b=2,c='westos')


结果:
1547992404.5991712
This is function test1
This is function test2
{'a': 1, 'b': 2, 'c': 'westos'}

装饰器实现一个函数计时器

  • 比较用for循环拼接,和系统内置拼接函数.join的速度
import time
import random
import string
import functools

li = [random.choice(string.ascii_letters) for i in range(10)]
print(li)
def timeit(fun):
    @functools.wraps(fun) ##为了保留被修饰的函数名和帮助信息文档
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = fun(*args,**kwargs)
        end_time = time.time()
        print('运行时间为:%.6f' %(end_time - start_time))
        return res
    return wrapper

@timeit
def con_add():
    """这是for循环"""
    s = ''
    for i in li:
        s += (i + ',')
    print(s)


@timeit
def join_add():
    """这是join方法"""
    print(','.join(li))

con_add()
join_add()

结果:
['O', 'e', 'P', 'g', 'D', 'S', 'L', 'z', 'D', 'g']
O,e,P,g,D,S,L,z,D,g,
运行时间为:0.000014
O,e,P,g,D,S,L,z,D,g
运行时间为:0.000007

装饰器问题1:被修饰的函数有返回值怎么办

  • 错误做法:
def timeit(fun): 
    @functools.wraps(fun)  
    def wrapper(*args,**kwargs):
        start_time = time.time()
        fun(*args,**kwargs)
        end_time = time.time()
        print('系统的运行时间为:%.6f' %(end_time - start_time))
    return wrapper


@timeit
def fun3(x):
    return x ** 2   ##被装饰的函数有返回值,则需要在装饰器中将函数的返回值传给一个参数,并返回这个参数,否则返回值为None

a = fun3(3)
print(a)

结果:
系统的运行时间为:0.000003
None
  • 正确做法:
def timeit(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = fun(*args,**kwargs)
        end_time = time.time()
        print('系统的运行时间为:%.6f' %(end_time - start_time))
		return res     ##返回函数的返回值
    return wrapper


@timeit
def fun3(x):
    return x ** 2    

a = fun3(3)
print(a)

装饰器问题2:如何保留被装饰函数的函数名和帮助信息文档

  • @functools.wraps(fun)
    添加这个东西

  • print(con_add.doc)
    打印帮助文档如果没有帮助文档,则返回None

  • print(con_add.name)
    打印函数名

  • 错误做法:

def timeit(fun):
    #@functools.wraps(fun)   ##如果不添加返回的帮助文档和信息都是wrapper的函数名和信息文档
    def wrapper(*args,**kwargs):
        '''这是一个wrapper'''
        start_time = time.time()
        res = fun(*args,**kwargs)
        end_time = time.time()
        print('系统的运行时间为:%.6f' %(end_time - start_time))
        return res
    return wrapper

@timeit
def fun3(x):
    '''这是一个func3'''
    return x ** 2

print(fun3.__doc__)
print(fun3.__name__)

结果:
这是一个wrapper
wrapper
  • 正确做法:
def timeit(fun):
    @functools.wraps(fun)  ##为了保留被修饰的函数名和帮助信息文档
    def wrapper(*args,**kwargs):
        '''这是一个wrapper'''
        start_time = time.time()
        res = fun(*args,**kwargs)
        end_time = time.time()
        print('系统的运行时间为:%.6f' %(end_time - start_time))
        return res
    return wrapper

@timeit
def fun3(x):
    '''这是一个func3'''   ##如果没有帮助文档(函数的注释),则返回None
    return x ** 2
 
print(fun3.__doc__)
print(fun3.__name__)
结果:
这是一个func3
fun3

多个装饰器的执行顺序

  • 以下我们用一个例子查看多个装饰器的执行顺序
def decorator_a(func):
    print('Get in decorator_a')
    def inner_a(*args,**kwargs):
        print('Get in inner_a')
        res = func(*args,**kwargs)     #返回值在两个定义的函数中都要定义
        return res
    return inner_a

def decorator_b(func):
    print('Get in decorator_b')
    def inner_b(*args,**kwargs):
        print('Get in inner_b')
        res = func(*args,**kwargs)   #返回值在两个定义的函数中都要定义,且这个返回值的结果在最后执行
        return res
    return inner_b

@decorator_b
@decorator_a     
def f(x):
    print('Get in f')
    return x * 2

print(f(1))

结果:
Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f
2
  • 例题:
    定义装饰器log,用户判断用户是否登陆成功
    定义装饰器isroot,用于判断用户是不是为root
    编写这2个装饰器:先判断是否登陆成功,再判断是不是root用户
import functools
import inspect


def is_root(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):
        #inspect.getcallargs返回值是字典,key值为:形参,value值为:形参对应实参
        inspect_res = inspect.getcallargs(fun,*args,**kwargs)
        print('inspect_res的返回值为:%s' %inspect_res)
        if inspect_res.get('name') == 'root':
            print('is root')
            res = fun(*args,**kwargs)
            return res
        else:
            print('not root user')
    return wrapper

li = ['root','westos','redhat']

def log(fun):
    @functools.wraps(fun)
    def inlog(name):
        if name in li:
            print('登陆成功')
            res = fun(name)
            return res
        else:
            print('登陆失败')
    return inlog


@log
@is_root

def add_student(name):   ##此处的name为inspect.getcallargs返回字典中的key值
    print('添加学生信息...')

add_student('root')

结果:
登陆成功                ##先判断登陆
inspect_res的返回值为:{'name': 'root'}   ##判断是不是root
is root
添加学生信息...     ##最后执行返回值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值