Python——装饰器的使用

一 什么是装饰器
装饰器的实现是函数里面嵌套函数
装饰器的本质是一个函数,它可以让其他函数在不需要作任何代码改动的前提下增加额外功能
装饰器需要传递一个函数,返回值也是一个函数对象
二 装饰器的应用
在函数执行之前和执行之后添加功能,调用函数的方式改变到了
不改变原有函数的调用方式:函数里面嵌套函数,并且返回嵌套的函数

def desc(fun):
    def addinfo():
        print('welcome')
        fun()
        print('seeyou')
    return addinfo
def login():
    print('login...')
login=desc(login)  #返回值是一个函数
login()

可以看到依次输出的是 welcome,login,seeyou
1) # 装饰器需求:获取函数执行的时间
函数执行之前的时间;函数执行之后的时间
看字符串拼接的效率:
1)相加
2)join 方法

import random
import string
import time

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

def timeit(fun):
    def wrapper():
        #函数执行之前
        start_time=time.time()
        #执行函数
        fun()
        #函数执行之后
        stop_time=time.time()
        print('time:%.6f'%(stop_time-start_time))
    return wrapper
@timeit
def con_add():
    s=''
    for i in li:
        s+=(i+',')
    print(s)
@timeit
def join_add():
    print(','.join(li))

con_add()
join_add()

可以比较两者的效率,join比+快点
2)检测列表生成式和map的效率高低,n为函数传入的参数
1)[i**2 for i in range(n)]
2) map(lambda x:x**2 range(n))

def timeit(fun):
    """这是一个装饰器timeit"""


    def wrapper(*args, **kwargs):  # 接收可变参数和关键字参数    假使传入的值n=5
        """这是一个wrapper函数"""
        # args:元组   kwargs:字典        此处就为 args=(5,)
        # 函数执行之前
        start_time = time.time()
        # 执行函数
        res = fun(*args, **kwargs)  # 需要传入的是一个值,显然元组是不行的,因此需要对参数进行解包:*args=5
        # 对于元组,字典进行解包
        # 函数执行之后
        stop_time = time.time()
        print('time:%.6f' % (stop_time - start_time))
        return res

    return wrapper


@timeit
def list_shi(n):
    """这是list函数"""
    return [i ** 2 for i in range(n)]


list_shi(1000)


@timeit
def map_shi(n):
    return (map(lambda x: x ** 2, range(n)))


map_shi(1000)
print(list_shi.__name__)
print(list_shi.__doc__)

这里写图片描述

可以看到map明显更快点,最后两行代码,输出的是list_shi的信息,但是最后却显示的是装饰器的信息,那么如何来保留被装饰函数的信息呢
这里就需要加入 functools模块

import time
import functools


def timeit(fun):
    """这是一个装饰器timeit"""

    @functools.wraps(fun)
    # 可以保留被装饰函数的函数名和帮助文档信息

    def wrapper(*args, **kwargs):  # 接收可变参数和关键字参数    假使传入的值n=5
        """这是一个wrapper函数"""
        # args:元组   kwargs:字典        此处就为 args=(5,)
        # 函数执行之前
        start_time = time.time()
        # 执行函数
        res = fun(*args, **kwargs)  # 需要传入的是一个值,显然元组是不行的,因此需要对参数进行解包:*args=5
        # 对于元组,字典进行解包
        # 函数执行之后
        stop_time = time.time()
        print('time:%.6f' % (stop_time - start_time))
        return res

    return wrapper


@timeit
def list_shi(n):
    """这是list函数"""
    return [i ** 2 for i in range(n)]


list_shi(1000)


@timeit
def map_shi(n):
    return (map(lambda x: x ** 2, range(n)))


map_shi(1000)
print(list_shi.__name__)
print(list_shi.__doc__)

这里写图片描述
这样可以看到输出的是被装饰函数的函数名和函数帮助文档
三 装饰器日志
创建装饰器 要求如下:
1.创建add_log装饰器被装饰的函数打印日志信息
2.日志格式为:[字符串时间],函数名:xxx,运行时间:xxx,运行返回值结果:xxx

import string
import time
import functools


def add_log(fun):
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = fun(*args, **kwargs)
        end_time = time.time()
        print('[%s]\n函数名:%s\n运行时间:%.5f\n运行返回值结果:%d'
              % (time.ctime(), add.__name__, end_time - start_time, res))
        # time.ctime()显示的时间为 Mon Aug 20 00:52:37 2018 这种格式
        return res

    return wrapper


@add_log
def add(x, y):
    time.sleep(0.1)  # 暂停0.1s
    return x + y


add(1, 2)

这里写图片描述
可以看到运行结果符合输出形式
四 装饰器登陆网站
需求:用户登陆验证装饰器id_login
1.如果用户登陆成功,则执行被装饰的函数
2.如果用户未登陆,则执行登陆函数

import functools

login_users = ['admin', 'root']


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)
        else:
            res = login()
        return res

    return wrapper


# 必须登陆成功
@is_login  # writeblog=is_login(writeblog)
def writeblog(name):
    return 'write blog'


def login():
    return 'login'


# 是否登陆都可以执行的函数
def news():
    print('news')


print(writeblog(name='admin1'))

这里写图片描述
可以看到输入的用户是列表中没有的,因此需要登陆后方可执行writeblog函数
五 装饰器判断变量的数据类型
需求:判断数据类型装饰器required_ints
确保函数接收到的每一个参数都是整形数
如果不是整形数,就报错

import functools


def required_ints(fun):
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):  #args=1,4
        for i in args:   #i=1
            if not isinstance(i, int):
                # isinstance一次只会判断一个i值,如果判断是的话,就执行fun,
                #       就会让后面的i,还没有判断,因此判断不是整形类型
                print('函数类型不完全正确')
                break
        else:
            res = fun(*args, **kwargs)
            return res

    return wrapper


@required_ints
def add(a, b):
    return a + b


@required_ints
def my_max(a, b, c, d):
    return max(a, b, c, d)


print(add(1, 4))

这里写图片描述
因输入的是两个整形,因此函数可以执行,且输出结果为5
六 带有多个装饰器的函数
需求1:用户登陆验证装饰器is_login
1)如果用户登陆成功,则执行被装饰的函数
2)如果用户未登陆,则执行登陆函数
需求2:判断用户是否为管理员,装饰器is_admin
1)如果是用户为管理员,则执行被装饰的函数
2)如果是用户不是管理员,则报错

import functools

login_users = ['admin', 'root']
def is_admin(fun):
    @functools.wraps(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)
        else:
            res = login()
        return res

    return wrapper


# 必须登陆成功
@is_login  # writeblog=is_login(writeblog)
@is_admin
def writeblog(name):
    return 'write blog'


def login():
    return 'login'


# 是否登陆都可以执行的函数
def news():
    print('news')


print(writeblog(name='root'))

这里写图片描述
就算为root用户,也是不可以,除非是admin用户,方才可以
那么含有多个装饰器时,它的运行过程又是怎样的

def make1(fun):
    print('blod1')

    def wrapper1(*args, **kwargs):
        print('blod2')
        return fun(*args, **kwargs)

    return wrapper1


def make2(fun):
    print('m1')

    def wrapper(*args, **kwargs):
        print('m2')
        return fun(*args, **kwargs)

    return wrapper


@make1  # login=make1(login)  #login为wrapper1
@make2  # login=make2(login)  #login为wrapper
def login():
    return '登陆'


print(login())

这里写图片描述
可以看到运行结果是依次打印的是 m1 blod1 blod2 m2 以及 登陆
也就是说:
当有多个装饰器时,从下到上调用装饰器
真实wrapper内容时从上到下执行的
七 带有参数的装饰器
编写装饰器,required_types
当装饰器为@required_types(int,float)时,确保函数接收到的每一个参数int或者float类型
当装饰器为@required_types(list)时,确保函数接收到的每一个参数list类型
当装饰器为@required_types(int,str)时,确保函数接收到的每一个参数int或者str类型
如果参数不满足,就报错,参数必须为xxx类型

import functools
import random


def required_types(*kinds):
    def required(fun):
        @functools.wraps(fun)
        def wrapper(*args, **kwargs):  #args=1,4
            for i in args:   #i=1
                if  not isinstance(i, kinds):
                    print('erorr,函数类型必须为',kinds)
                    break
                else:
                    res = fun(*args, **kwargs)
                    return res
        return wrapper
    return required
@required_types(int,float)
def add(a, b):
    return a + b


print(add(1,2.0))

这里写图片描述
因参数满足条件,因此输出结果为 3.0
八 动图处理

import random
from  PIL import  Image, ImageSequence


with Image.open('cat.gif') as im:
    print(im.is_animated, im.n_frames)
    # 判断图片是否为动图
    if im.is_animated:
        # 列表生成式
        frames = [f.copy() for f in ImageSequence.Iterator(im)]
        # 对列表反转
        # frames = frames[::-1]  #两者都可以
        # frames.reverse()

        # 对于动图的帧列表元素打乱顺序
        random.shuffle(frames)
        frames[0].save('out.gif', save_all=True, append_images=frames[1:])


    #功能: 将动图的每一帧保存为一个图片;
    index = 1
    for frame in ImageSequence.Iterator(im):
        print("image %d, mode: %s, size:%s" %(index, frame.mode, frame.size))
        frame.save('img/frame%d.png' %(index))
        index += 1

这里写图片描述
可以看到被打乱的gif图片被保存至了img目录下面

猜你喜欢

转载自blog.csdn.net/weixin_42668123/article/details/81940631