python-装饰器

一、 什么是装饰器?

把一个函数当作参数传递给另外一个函数,返回一个替代版的函数,本质上就是一个返回函数的函数。在不改变原函数的基础上,给函数增加功能。

二、装饰器的入门

定义一个打印hello!的函数,如何在打印结果之前加*******?
1.使用语法糖:

def outer(f):#装饰器,写好之后可被多个函数使用
    def inner():
        print('*****')
        f()
    return inner

@outer #语法糖
def fun():
    print('hello!')
fun()

@outer#语法糖,第二个函数也可以使用
def g():
    print('星期六')
g()

在这里插入图片描述
2.不使用语法糖

def outer(f):  #装饰器
    def inner():
        print('*****')
        f()
    return inner

def fun():#原本的函数
    print('hello!')
    
a=outer(fun) #定义一个新的函数outer,fun是参数
a()   #函数调用

在这里插入图片描述

三、bi站学的装饰器例子

**eg1:**打印10000以内的质数并计时

import time
def is_prime(num):#判断是否是质数
    if num<2:
        return False
    elif num==2:
        return True
    else:
        for i in range(2,num):
            if num%i==0:
                return False
        return True
def prime_nums():#打印10000以内的质数并计时
    t1=time.time()
    for i in range(2,10000):
        if is_prime(i):
            print(i)
    t2=time.time()
    print(t2-t1)
prime_nums()

结果:
在这里插入图片描述
如果想要计算每个函数的运行时长,需要在每一个函数里面都加time 计时,一个两个函数还好,但工作后如果几千个函数,这就很麻烦了。所以eg1不建议使用。

**eg2:**引进无参装饰器

import time
def dis_play(fun): #装饰器
    def wrapper():
        t1=time.time()
        fun()
        t2=time.time()
        print('运行时间为:%s' % (t2 - t1))
    return wrapper

def is_prime(num): #判断是否是质数
    if num<2:
        return False
    elif num==2:
        return True
    else:
        for i in range(2,num):
            if num%i==0:
                return False
        return True
@dis_play #语法糖
def prime_nums():
    for i in range(2,10000):
        if is_prime(i):
            print(i)
prime_nums()

结果:
在这里插入图片描述
**eg3:**引进有参装饰器,还是上个例子,打印处10000以内质数的个数,就是说有返回值参数了。

import time
def dis_play(fun): #装饰器
    def wrapper():
        t1=time.time()
        res=fun()  
        t2=time.time()
        print('运行时间为:%s' % (t2 - t1))
        return res  #函数有返回值时,需将返回值return一下
    return wrapper

def is_prime(num): #判断是否是质数
    if num<2:
        return False
    elif num==2:
        return True
    else:
        for i in range(2,num):
            if num%i==0:
                return False
        return True
@dis_play #语法糖
def prime_nums_count():
    count=0
    for i in range(2,10000):
        if is_prime(i):
            count+=1
    return count
a=prime_nums_count()
print(a)

结果:
在这里插入图片描述升级版:任意范围内质数的个数

import time
def dis_play(fun): #装饰器
    def wrapper(*args):  #传入参数
        t1=time.time()
        res=fun(*args)  
        t2=time.time()
        print('运行时间为:%s' % (t2 - t1))
        return res
    return wrapper

def is_prime(num): #判断是否是质数
    if num<2:
        return False
    elif num==2:
        return True
    else:
        for i in range(2,num):
            if num%i==0:
                return False
        return True
@dis_play #语法糖
def prime_nums_count(maxnum):  
    count=0
    for i in range(2,maxnum):
        if is_prime(i):
            count+=1
    return count
a=prime_nums_count(10000)
print(a)

在这里插入图片描述

四、如何保留被装饰函数的函数名字和帮助文档信息?

在装饰器函数的下面(闭合函数的上面加:@functools.wraps(fun))

import functools
import random
import string
import time
li = [random.choice(string.ascii_letters) for i in range(100)]
print(time.time()) #打印处自1970年到现在一共多少秒
def timeit(fun):
    # @functools.wraps(fun)
    def wrapper(*args,**kwargs): #接收可变参数 和 关键字参数
        """这是一个装饰器timeit"""
        # 在函数执行之前
        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():
    """这是一个con_add"""
    s = ''
    for i in li:
        s +=i
    print(s)
con_add()
print(con_add.__name__)
print(con_add.__doc__)

在这里插入图片描述如果不加@functools.wraps(fun),则显示的是装饰器的信息
在这里插入图片描述

五、多个装饰器

多个装饰器装饰函数 从上到下去执行的

def decorator_a(fun):
    def inner_a(*args,**kwargs): #接收可变参数和关键字参数
        print('Get in inner_a')
        fun(*args,**kwargs)
    return inner_a

def decorator_b(fun):
    def inner_b(*args,**kwargs):
        print('Get in inner_b')
        fun(*args,**kwargs)
    return inner_b

# 多个装饰器装饰函数 从上到下去执行的
@decorator_a
@decorator_b
def f(x):
    print('Gat in f')
f(1)

在这里插入图片描述

六、装饰器的应用

多个装饰器的应用场景
会采用多个装饰器先验证是否登陆成功 再验证权限是否足够

import inspect
login_session = ['root','admin','redhat']
import functools
def is_login(fun):
    @functools.wraps(fun)
    def warapper(*args,**kwargs):#('root',)如果有多个可变参数,args会存成元组
        print(args)
        if args[0] in login_session:
            temp = fun(*args,**kwargs)
            return temp
        else:
            print()
            print('Error:%s 没有登陆成功' %(args[0]))
    return warapper
def is_admin(fun):
    @functools.wraps(fun)
    #inspect_res 会返回一个字典
    # key:形参 value:对应的实参
    def wrapper(*args,**kwargs):
        inspect_res = inspect.getcallargs(fun,*args,**kwargs)
        print('inspect的返回值是:%s' %(inspect_res))
        if inspect_res.get('name') == 'root':
            temp = fun(*args,**kwargs)
            return temp
        else:
            print('not root user,no permisson add user')
    return wrapper
@is_login
@is_admin
def adduser(name):
    print('add_user')
adduser('refhat')

结果:
在这里插入图片描述
牛刀小试:
练习1:
创建装饰器, 要求如下:

  1. 创建add_log装饰器, 被装饰的函数打印日志信息;
  2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx,
    运行返回值结果:xxx·
import functools
import random
import time
def outer(f):
    @functools.wraps(f) #显示被修饰函数的函数名,注释文档信息
    def innner():
        """
        这是一个装饰器函数
        """
        t1=time.time()
        a=f()
        s_time=time.ctime()
        t2=time.time()
        print('字符串时间:'+s_time)
        print('运行时间:%.3fs' %(t2-t1))
        print('函数名:%s' %(f.__name__))
        print('函数返回值结果:%s' %(a))
    return innner
@outer  #语法糖
def fun():
    st=input('请输入自符:')
    return st
fun()

练习2:
基础版(无参数的装饰器)
编写装饰器required_ints, 条件如下:
1). 确保函数接收到的每一个参数都是整数;
2). 如果参数不是整形数, 打印 TypeError:参数必须为整形

import functools
def required_ints(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):  # args=(1,2,..)
        for i in args:
            if not isinstance(i, str):
                print('函数所有的参数并非都是int型')
                break
        else:
            res = func(*args, **kwargs)
            return res
    return wrapper
@required_ints
def add(a, b):
    print(a + b)
add(1,2.0)

在这里插入图片描述
练习3:
2. 升级版(有参数的装饰器)
编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的每一个参数都是
int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类型

import functools
def required_type(*kind):
    def out(fun):
        @functools.wraps(fun)
        def wrapper(*args, **kwargs):  # args=(1,2,..)
            for i in args:
                if not isinstance(i,kind):
                    print('函数所有的参数并非都是题目所要求类型')
                    break
            else:
                res = fun(*args, **kwargs)
                return res
        return wrapper
    return out
@required_type(int,str)
def add(a, b):
    return a,b
add(1,2.0)

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值