高阶函数之装饰器

1.定义

装饰器就是用来装饰函数。
想要增强原有函数的功能;
但不希望修改now()函数的定义;
在代码运行期间动态增加功能的方式

2.创建装饰器

def desc(fun):  # fun=login    需要传递一个函数,即要装饰的函数
    def add_info():   ###装饰器函数里面嵌套函数
        print('你好')
        fun()   ###login()
        print('欢迎再光临')
    return add_info     ##返回值是嵌套函数

@desc
def login():
    print('login...')

login()

3.装饰器的应用场景

1)装饰器实现计时器

需求:获取每个函数执行的时间
例1:看字符串拼接的效率
‘hello’+’world’
” “.join([‘hello’,’world’])
例2:查看生成列表的快慢
list
map

import random
import time
import string
li=[random.choice(string.ascii_letters) for i in range(1,100)]


def timeit(fun): # con_add
    def wrapper(*args,**kwargs):###接收可变参数和关键字参数
        # 函数执行前
        start_time = time.time()
        # 执行函数
        fun(*args,**kwargs)##args解包
        # 函数执行后
        end_time = time.time()
        print("运行时间为:%.6f" % (end_time - start_time))
    return  wrapper


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

@timeit
def join_add():
    print(",".join(li))
join_add()




@timeit
def con_list(n):
    print([i*2 for i in range(n)])
con_list(50)

@timeit
def con_map(n):
    print (list(map(lambda x:x*2 , range(n))))
con_map(50)

这里写图片描述
引入问题
1.函数有返回值的解决办法
把执行函数的值赋给一个变量,最后在装饰函数的嵌套函数返回这个值即可

import random
import time
import string

def timeit(fun): # con_add
    def wrapper(*args,**kwargs):###接收可变参数和关键字参数
        # 函数执行前
        start_time = time.time()
        # 执行函数
        res=fun(*args,**kwargs)##args解包
        # 函数执行后
        end_time = time.time()
        print("运行时间为:%.6f" % (end_time - start_time))
        return res
    return  wrapper


@timeit
def con_list(n):
    return ([i*2 for i in range(n)])
print(con_list(50))

@timeit
def con_map(n):
    return list(map(lambda x:x*2 , range(n)))
print(con_map(50))

这里写图片描述
2.保留被装饰函数的函数名和帮助文档信息
这里需要导入funtools模块,且要执行functools.wraps(fun) 命令(当然这个fun根据你的定义写)

import random
import time
import string
import functools
def timeit(fun): # con_add
    @functools.wraps(fun)           ##保留被装饰函数的函数名和帮助文档信息
    def wrapper(*args,**kwargs):###接收可变参数和关键字参数
        # 函数执行前
        start_time = time.time()
        # 执行函数
        res=fun(*args,**kwargs)##args解包
        # 函数执行后
        end_time = time.time()
        print("运行时间为:%.6f" % (end_time - start_time))
        return res
    return  wrapper

@timeit
def con_list(n):
    print([i*2 for i in range(n)])
print(con_list(50))

@timeit
def con_map(n):
    return list((map(lambda x:x*2 , range(n))))
print(con_map(50))


print(con_list.__name__)
print(con_list.__doc__)

这里写图片描述
2)查看日志信息

需求:创建add_log装饰器,被装饰的函数打印日志信息;
日志格式为:字符串时间 函数名;运行时间;运行返回值结果

import time
import functools


def add_log(fun):
    @functools.wraps(fun)
    def wrapper(*args,**kwargs):

        ctime=time.ctime()   ###字符串时间
        fun_name=fun.__name__

        start_time = time.time()
        res=fun(*args,**kwargs)
        end_time=time.time()
        print("[%s] 函数名:%s 运行时间为:%.6f 运行返回结果:%s" % (ctime,fun_name,end_time - start_time,res))
        return res
    return wrapper

@add_log
def logmessags():
    '''日志信息'''
    return 'hello'

logmessags()

这里写图片描述
3)用户登陆验证(装饰器的第二种方式)

如果用户登陆成功,则执行被装饰的函数
如果用户登陆不成功,则执行登陆函数

import functools
login_users=['root','admin']
def is_login(fun):  # fun: writeBlog
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):   # name="admin"  # kwargs={"name":"admin"}
        # 判断写博客的这个用户是否登陆成功
        if kwargs.get("name") in login_users:
            res = fun(*args, **kwargs)
            return res
        else:
            res=login()
        return res
    return wrapper

@is_login
# 必须登陆成功
def writeBlog(name):
    return '写博客'
def login():
    return '你要登陆阿'
# 是否登陆成功均可进入
def news():
    print('新闻/。。。')


print(writeBlog(name='admin'))

这里写图片描述

4.无参数的装饰器

需求:
1)确保函数接到的每一个参数均为整数
2)如果参数不是整形数,打印TypeError:参数必须为整形

import functools
def required_ints(fun):
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):
        for i in args:
            if not  isinstance(i,int):
                return 'TypeError: 参数必须为整形'
                break
        else:
            res=fun(*args,**kwargs)
            return res
    return wrapper
@required_ints
def add(a,b):
    return a+b
print(add(1,1.0))
@required_ints
def Max(a,b,c,d):
    return max(a,b,c,d)
print(Max(1,2,3,4))

5.含参装饰器

例:对上例的升级版
编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的每一个参数都是int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxx

import functools
def required_types(*kinds):  # kinds
    def required(fun):
        @functools.wraps(fun)
        def wrapper(*args, **kwargs):  # args=(1,2)
            for i in args: # i=1
                # if isinstance(i, int):
                #     pass
                # else:
                #     print("函数所有参数并非为整形")
                #     break
                if not isinstance(i , kinds):
                    print("函数所有参数并非", kinds)
                    break
            else:
                res = fun(*args, **kwargs)
                return  res
        return wrapper
    return  required

@required_types(int, float)
def add(a, b):
    return a + b

@required_types(int)
def myMax(a, b, c, d):
    return max(a, b, c, d)
print(add(1,2.0))

例2:
建装饰器,要求如下:
日志格式为:日志等级:字符串时间 函数名;运行时间;运行返回值结果

import time
import functools
def log(kind):  ##kind='debug'
    def add_log(fun):
        @functools.wraps(fun)
        def wrapper(*args,**kwargs):

            ctime=time.ctime()
            fun_name=fun.__name__
            start_time = time.time()
            res=fun(*args,**kwargs)
            end_time=time.time()
            print("<%s> [%s] 函数名:%s 运行时间为:%.6f 运行返回结果:%s" % (kind,ctime,fun_name,end_time - start_time,res))
            return res
        return wrapper
    return add_log

@log('debug')
##log('debug')==>返回值是add_log
##messages=add_log(messages)
def logmessags():
    '''日志信息'''
    return 'hello'

logmessags()

有无参数的区别在于装饰的函数后跟不跟参数

@required_ints
def add(a,b):
    return a+b
print(add(1,1.0))
@log('debug')
def logmessags():
    '''日志信息'''
    return 'hello'

6.含有多个装饰器

##当有多个装饰器时,从下到上调用装饰器
##真实wrapper内容执行时从上到下执行
import functools
def makebold(fun):
    print("bold1")
    def wrapper1(*args, **kwargs):
        print("bold2")
        return fun(*args, **kwargs)  # wrapper

    return wrapper1

def makei(fun):  # fun=login
    print("i1")

    def wrapper(*args, **kwargs):
        print("i2")
        return fun(*args, **kwargs)
    return wrapper


##当有多个装饰器时,从下到上调用装饰器
##真实wrapper内容执行时从上到下执行
@makebold
@makei
def login():
    return '登陆'
print(login())

这里写图片描述
例;需求:用户登录验证的装饰器
1)如果用户登陆成功,则执行被装饰的函数
2)如果用户登陆不成功,则执行登陆函数

2.需求:判断登陆的用户是否为管理员(此处管理员只有一个)
1)如果用户为管理员,则执行被装饰的函数
2)如果用户不是管理员,则报错

import functools
login_users=['root','admin']
def is_admin(fun):
    def wrapper(*args,**kwargs):
        if kwargs.get('name')=='admin':
            res=fun(*args,**kwargs)
            return res
        else:
            return 'Error:你们没有权限'
    return wrapper
def is_login(fun):  # fun: writeBlog
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):   # name="admin"  # kwargs={"name":"admin"}
        # 判断写博客的这个用户是否登陆成功
        if kwargs.get("name") in login_users:
            res = fun(*args, **kwargs)
            return res
        else:
            res=login()
        return res
    return wrapper
@is_admin
@is_login
# 必须登陆成功
def writeBlog(name):
    return '写博客'
def login():
    return '你要登陆阿'
# 是否登陆成功均可进入
def news():
    print('新闻/。。。')


print(writeBlog(name='admin'))

这里写图片描述

猜你喜欢

转载自blog.csdn.net/forever_wen/article/details/81900841