python 装饰器

以下所有的实操全在redhat7.3真机上pycharm


装饰器

装饰器作用:不改变原函数,给原函数的功能进行增加新功能或修改;
装饰器(decorator):
把一个函数当作参数,返回一个替代版的函数
本质上,decorator就是一个返回函数的高阶函数
@decorator:语法塘
@functools.wraps(函数名):获取原函数的帮助手册,成为原函数的帮助手册,如果不写的话得到是装饰器的帮助手册;
“”"

给一个原函数前添加一个是新功能,但是必须调用这个原函数且不许改变原函数的本质

#1、普通的函数调用实现,但是它没有直接调用原函数,违背了需求
def fun1():
    print('wang')
def fun2():
    print('yi')
    fun1()
a = fun2()

在这里插入图片描述
#2、利用装饰器调用,无参数的状态

import time
def decorator(func):
#decorator()就是一个装饰器,所以接受一个函数作为参数,并返回一个函数;这里的形参名是函数名可以任意
    def wrapper():   #去找wrapper()函数
        print(time.time())
        func()         #这里调用的是原函数f1()函数
    return wrapper
#在wrapper()函数内,首先打印日志,再紧接着调用原始函数。

#1.如果这里没有返回函数,那么这个wrapper()函数也就没有什么作用了;
#2.这里必须返回函数名,然后去调用wrapper函数,如果返回函数则是返回的是返回值,就不能调用原函数,违反了需求;

@decorator  #装饰器   这里@decorator相当于decorator(f1()),注意:考虑代码的顺序;
def f1():    #这里的函数名就是实参
    print('This is a function')
#f = decorator(f1)#注释掉@decorator,用这种方式也可以实现;但是它不是直接调用的原函数
#f()
f1()

在这里插入图片描述
#指定参数
代码:

import time
def decorator(func):#这里的形参名是函数名可以任意
    def wrapper(*args):   #去找wrapper()函数
        print(time.time())
        func(*args)         #这里调用的是f2()函数
    return wrapper   #这里必须返回函数名,然后去调用wrapper函数,如果返回函数则是返回的是返回值,就不能调用原函数,违反了需求;

@decorator
def f2(func_name1,func_name2):
    print('This is function ' + func_name1)
    print('wangyibo is handsome ' + func_name2)
f2('zhao','hai')

运行结果:
在这里插入图片描述

#存在字典参数
代码:

import time
def decorator(func):#这里的形参名是函数名可以任意
    def wrapper(*args,**kwargs):   #去找wrapper()函数
        print(time.time())
        func(*args,**kwargs)         #这里调用的是f3()函数
    return wrapper
#wrapper()函数的参数定义是(*args,**kwargs),因此,wrapper()函数可以接受任意参数的调用。
##在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
#
##1.如果这里没有返回函数,那么这个wrapper()函数也就没有什么作用了;
#2.这里必须返回函数名,然后去调用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' )

运行结果:
在这里插入图片描述

装饰器计时

import functools  #函数名和帮助手册;	import	functools是导入functools模块。
import random
import string
import time

li = [random.choice(string.ascii_letters) for i in range(5)]
#print(li)

def timecal(func):#2-2
    """这是一个装饰器"""

    @functools.wraps(func)
    #只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。
    #如果不写@functools.wraps(func),那么对于__name__等属性,
    # 经过装饰器装饰的函数con_add()函数的函数名__name__等属性,就变成了wrapper()函数名
    #解决办法就是添加 @functools.wraps(func)
    def wrapper(*args,**kwargs):  #3-2
        """这是wrapper函数"""
        start_time = time.time() #4
        res = func(*args,**kwargs)#5   #调用了con_add()函数 #7-2
        # print('con_add',res)
        end_time =time.time()  #8
        print('运行时间为:%.8f' %(end_time-start_time))  #9
        return res    #将用运行结果返回到函数的调用  #10-1
    return wrapper  #3-1
@timecal #2-1
def con_add():#6
    """这是con_add函数"""
    s = ''   #声明s为空字符
    for i in li:
        s += (i + ',')   #将字符串拼接起来
    #测试: print('s:',s)
    return s  #返回给res  #7-1
@timecal
def join_add():
    """这是join_add函数"""
    return ','.join(li)

a = con_add()#1 #10-2
#测试:print('res:',a)
print(a)

在这里插入图片描述
#比较for循环与连接时间那个快,连接时间快

con_add()
join_add()

在这里插入图片描述

print(con_add.__name__)#输出函数名
print(con_add.__doc__) #输出功能注释

在这里插入图片描述

装饰器练习:

需求:
#创建装饰器, 要求如下:
#1. 创建add_log装饰器, 被装饰的函数打印日志信息;
#2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx,
运行返回值结果:xxx

代码:

import time
import functools

def add_log(func):
    @functools.wraps(func)
    def wrapper(*args,**kwargs):
        start_time = time.time()
        res = func(*args,**kwargs)
        end_time=time.time()
        print('[%s]函数名:%s,运行时间:%.6f,运行返回值结果:%d'
              %(time.ctime(),func.__name__,end_time-start_time,res)) #time.ctime():更好辨认
        return res
    return wrapper
@add_log
def add(x,y):
    time.sleep(1)
    return x+y
add(1,2)

运行结果:
在这里插入图片描述
练习:
判断是否登录,只需要判断用户是否在这个列表里即可

#2.登录用户是否为超户
import functools
import inspect

def is_admin(fun):
    @functools.wraps(fun)
    def wrapper(*args):
        #print('res:',args[0])
	   #inspect.getcallargs返回一个字典,key值:形参 value值:对应的实参
        inspect_res = inspect.getcallargs(fun,*args)
        print('inspect的返回值:%s' %inspect_res)
 	    #返回值是字典是由于inspect.getcallargs()自动生成的key-values对,
        #add_student('root')函数调用传的实参root就是values值,
        # 而key值是函数inspect.getcallargs()自动生成的调用的原函数add_student(name)的形参name)
        if inspect_res.get('name') == 'root':
            res = fun(*args)
            return res
        else:
            print('Permission denied!')
    return wrapper
#1,判断是否登录
login_user = ['root','admin','redhat']
def is_login_user(fun):
    @functools.wraps(fun)
    def wrapper(*args):
        if args[0] in login_user:
            print('%s登录成功' %args[0])
            res = fun(*args)
            # print('res:',res)
            return res
        else:
            print('登录失败!')
    return wrapper
@is_login_user
@is_admin
def add_student(name):  #name是形参
    print('添加学生%s...' %name)

add_student('root')

在这里插入图片描述
add_student(‘westos’)
在这里插入图片描述

带参数的装饰器

编写装饰器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_types(*kinds):
    def requied(fun):
        @functools.wraps(fun)
        def wrapper(*args,**kwargs):
            for i in args:
                if not isinstance(i,kinds): #isinstance(i,kind):判断对象i类型与kind类型相同返回True,否则False
                   #print('TypeError:参数必须为',kinds)
                   raise TypeError('参数必须为%s,%s' % kinds) #raise:抛出异常
            else:
                res =fun(*args,**kwargs)
                return res
        return wrapper
    return requied
@required_types(float,int) #多层嵌套相当于:required_types(float,int)(add(a,b))
#传的是类型至少是两个类型中的一个,
def add(a,b):
    return a+b

print(add(1.0,1.2))

在这里插入图片描述
将上面的代码:@required_types(int,int)
在这里插入图片描述

多个装饰器

#多个装饰器 相当于装饰器函数的嵌套
从上往下调用,嵌套在一起,一层一层执行

def decorator1(fun1):
    def inner1(*args,**kwargs):  #**kwargs就是个摆设,无用,也可以不写,只是一贯的套路
        print('w1 ,before')
        fun1()
        print('w1 after')

    return inner1
def decorator2(fun2):
    def inner2(*args,**kwargs):
        print('This is inner2')
        print('w2 ,before')
        fun2()
        print('w2 after')

    return inner2

@decorator2   #f =decorator2(f) ,fun2 =inner1函数 当执行完fun2时,就代表要去执行inner1,fun1()执行的是真正的原函数
@decorator1   #f = decorator1 (f),fun1=f原函数 f()=inner1
def f():
    print('This is f')
f()

#推荐一种方式 ,debug模式.
#多个装饰器装饰一个函数相当于装饰器函数的嵌套 (相当于把另一个装饰器函数的inner方法作为参数传递给了另一个装饰器函数的形参func接收.) so不明白就看下面吧?
#1. 程序从上到下执行, 加载两个函数到内存. —> 按常理来说应该是先执行decorator2,但是由于decorator1 靠近被装饰的原函数. so 先执行 decorator1装饰器函数
#2. 执行 decorator1装饰器,fun1 形参接收的是f原函数的内存地址, 而f(变量)被指向为—>inner1函数内存地址
#3. 指向decorator2装饰器 , fun2 形参接收的是 f(变量的内存地址 ,也就是inner1的内存地址), 此时的f(变量)被重新指向为inner2 函数的内存地址.

#加载执行过程:
#f(变量)—>inner1,func1形参—>f原函数 ==========>>>> f(变量)----<inner2 ,func2形参---->inner1(inner1函数内存地址被当做参数传给了func2)

#取值执行过程:
#f(变量)就是inner2 函数执行, ----> 打印w2 before -->执行 fun2, 也就是执行inner1函数,打印w1 before
#----->执行fun1形参的值,fun1指向的 f原函数 ,打印 f原函数内的东西 ----> 执行完原函数后,返回到inner1函数继续执行
#----->打印inner1函数 w1 after .------> 执行完inner1函数,返回到inner2函数中,打印 w2 after

#1.哪个装饰器在被装饰器函数的最上面.那个就是最外层.
#2.一层一层嵌套. 如下图
在这里插入图片描述

运行结果:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值