zoukankan      html  css  js  c++  java
  • python装饰器

    python装饰器


    一 什么是装饰器

    器指函数
    装饰即修饰,意指为其他函数添加新功能
    装饰器定义:本质就是函数,功能是为其他函数添加新功能
    

    二 装饰器需要遵循的原则

    1.不能修改被修饰函数的源代码
    2.不能修改被修饰函数的调用方式

    现在有下面一个函数,如果我们要为这个函数添加一个功能:测试程序的运行时间。

    import time
    list=[1,2,3,4,5]
    def cal(list):
        for i in list:
            time.sleep(0.5)
            print(i)
    cal(list)
    import time
    list=[1,2,3,4,5]
    def cal(list):
        start_time=time.time()
        for i in list:
            time.sleep(0.5)
            print(i)
        stop_time=time.time()
        print("运行时间:%s" %(stop_time-start_time))
    
    cal(list)
    1
    2
    3
    4
    5
    运行时间:2.502134323120117
    运行结果

    总结:我们虽然实现了功能,但是我们要知道两大原则,现在我们直接修改函数的源代码,这样做是不行的。

    三 实现装饰器知识储备

    装饰器=高阶函数+函数嵌套+闭包

    四 高阶函数

    高阶函数定义:
    1.函数接收的参数是一个函数名

    2.函数的返回值是一个函数名

    3.满足上述条件任意一个,都可称之为高阶函数

    def foo():
        print('我的函数名作为参数传给高阶函数')
    def gao_jie1(func):
        print('我就是高阶函数1,我接收的参数名是%s' %func)
        func()
    
    def gao_jie2(func):
        print('我就是高阶函数2,我的返回值是%s' %func)
        return func
    
    gao_jie1(foo)
    gao_jie2(foo)
    高阶函数示例

    只满足一个条件:函数接收的参数是一个函数名

    import time
    def foo():
        print('from the foo')
    
    def timmer(func):
        start_time=time.time()
        func()
        stop_time=time.time()
        print('函数%s 运行时间是%s' %(func,stop_time-start_time))
    timmer(foo)
    #总结:我们确实为函数foo增加了foo运行时间的功能,但是foo原来的执行方式是foo(),现在我们需要调用高阶函数timmer(foo),改变了函数的调用方式
    View Code

    满足两个条件

    1.函数接收的参数是一个函数名
    2.函数的返回值是一个函数名

    import time
    def foo():
        print('from the foo')
    
    def timmer(func):
        start_time=time.time()
        return func
        stop_time=time.time()
        print('函数%s 运行时间是%s' %(func,stop_time-start_time))
    foo=timmer(foo)
    foo()
    #总结:我们确实没有改变foo的调用方式,但是我们也没有为foo增加任何新功能
    View Code

    高阶函数总结
    1.函数接收的参数是一个函数名
      作用:在不修改函数源代码的前提下,为函数添加新功能,
      不足:会改变函数的调用方式
    2.函数的返回值是一个函数名
      作用:不修改函数的调用方式
      不足:不能添加新功能

    五 函数嵌套

    def father(name):
        print('from father %s' %name)
        def son():
            print('from son')
            def grandson():
                print('from grandson')
            grandson()
        son()
    
    father('aaa')

    六 闭包

    闭包:  就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁
    闭包:闭包是指内部函数调用外部函数的变量
    包是指嵌套,闭是指封装
    def test1():
    	a=1
    	def test2():
    		print(a)
    	test2()
    test1()
    

      

    七 无参装饰器

    无参装饰器=高级函数+函数嵌套

    基本框架

    1 #这就是一个实现一个装饰器最基本的架子
    2 def timer(func):
    3     def wrapper():
    4         func()
    5     return wrapper

     

    加上参数

    解决被修饰的函数是有参数的。
    1 def timer(func):
    2     def wrapper(*args,**kwargs):
    3         func(*args,**kwargs)
    4     return wrapper
    import time
    def timmer(func):#func=test
        def wrapper(name,age):
            start_time = time.time()
            res=func(name,age)  # 就是在运行test()
            stop_time = time.time()
            print("函数运行时间的是%s" % (stop_time - start_time))
            return res
        return wrapper
    # @timmer 等效于 test=timmer(test)
    @timmer
    def test(name,age):
        time.sleep(3)
        print('test函数运行完毕')
        return "这是test的返回值"
    res=test("lingling",20)
    print(res)
    
    
    @timmer
    # @timmer 等效于 test1=timmer(test1)
    def test1(name,age,gender):
        time.sleep(3)
        print('test函数运行完毕')
        return "这是test1的返回值"
    res=test1("lingling",20)
    print(res)
    #@timmer 等效于 test=timmer(test),#在这里一定要注意,我们首先把test函数传入到timmer装饰器,我们下一步返回了wrapper函数的地址(return wrapper)。
    #test变量接收到wrapper函数的地址
    #test("lingling",20)传入参数,执行wrapper函数,这里test函数有参数,为name,age,所以wrapper函数也应该有两个变量name,age,而执行wrapper函数,实际是要执行test函数,此处也需要两个参数,name,age
    #res变量接收到test函数执行的结果
    
    
    
    # 1.将test函数传入timmer:timmer(test)
    # 2.执行timmer函数,返回wrapper函数地址(return wrapper)
    # 3.执行wrapper函数(执行wrapper函数实则是执行test函数),并接收test函数返回值:res=test("lingling",20)
    参数个数不同
    import time
    def timmer(func):#func=test
        def wrapper(*wargs,**kwargs):
            start_time = time.time()
            res=func(*wargs,**kwargs)  # 就是在运行test()
            stop_time = time.time()
            print("函数运行时间的是%s" % (stop_time - start_time))
            return res
        return wrapper
    
    @timmer
    def test(name,age):
        time.sleep(3)
        print('test函数运行完毕')
        return "这是test的返回值"
    res=test("lingling",age=20)
    print(res)
    
    
    @timmer
    def test1(name,age,gender):
        time.sleep(3)
        print('test1函数运行完毕')
        return "这是test1的返回值"
    res=test1("lingling",20,'male')
    print(res)
    
    #解决wrapper不固定接收参数的问题
    #如果细心可能会想,这里我们把wrapper参数写死了,你只能传入两个参数,如果有不同的函数需要这个装饰器,那我们不得重新修改装饰器,这真是一个大问题,我们来解决这个问题
    #所以我们在wrapper函数中应该接收到test函数的所有参数:*wargs,**kwargs
    # *wargs:接收所有位置参数
    # **kwargs:接收键值对类型的参数
    #我们将wrapper函数与内部func函数的参数设置*wargs,**kwargs就可以接收所有传进来的参数
    
    #第二个问题,func接收参数
    
    # 使用*wargs,**kwargs怎么接收参数
    # 就使用*wargs,**kwargs怎么传递参数
    # 我们可以把*wargs,**kwargs看成一个整体,接收所有的参数
    解决接收不同个数的参数
    def test2(name,age,gender): #test2(*('alex',18,'male'),**{})
        print(name)
        print(age)
        print(gender)
        
    def test1(*args,**kwargs):
        test2(*args,**kwargs) #args=('alex',18,'male')  kwargs={}
    test1('alex',18,'male')
    
    
    def test3(*args,**kwargs):
        print(args)
        print(kwargs)
    #
    test3(1,2,3,4,name="linlin")
    test3(*(1,2,3,4),**{'name':'linlin'})
    args与kwargs的补充

    加上功能

    在装饰器的框架基础上添加被测试函数所需要的功能。
    import time
    def timer(func):
        def wrapper(*args,**kwargs):
            start_time=time.time()
            func(*args,**kwargs)
            stop_time=time.time()
            print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time))
        return wrapper
    import time
    def timmer(func):#func=test
        def wrapper():
            start_time = time.time()
            func()  # 就是在运行test()
            stop_time = time.time()
            print("函数运行时间的是%s" % (stop_time - start_time))
        return wrapper
    # @timmer 等效于 test=timmer(test)
    @timmer
    def test():
        time.sleep(3)
        print('test函数运行完毕')
    test()
    import time
    def timmer(func):#func=test
        def wrapper():
            start_time = time.time()
            func()  # 就是在运行test()
            stop_time = time.time()
            print("函数运行时间的是%s" % (stop_time - start_time))
        return wrapper
    
    
    def test():
        time.sleep(3)
        print('test函数运行完毕')
    test=timmer(test)#返回的是wrapper的地址
    test()#执行的是wrapper()
    原型

     

    加上返回值

    解决在测试程序里有返回值的情况。
    import time
    def timer(func):
        def wrapper(*args,**kwargs):
            start_time=time.time()
            res=func(*args,**kwargs)
            stop_time=time.time()
            print('函数[%s],运行时间是[%s]' %(func,stop_time-start_time))
            return res
        return wrapper
    import time
    def timmer(func):#func=test
        def wrapper():
            start_time = time.time()
            res=func()  # 就是在运行test()
            stop_time = time.time()
            print("函数运行时间的是%s" % (stop_time - start_time))
            return res
        return wrapper
    # @timmer 等效于 test=timmer(test)
    @timmer
    def test():
        time.sleep(3)
        print('test函数运行完毕')
        return "这是test的返回值"
    res=test()
    print(res)

     

    语法糖@

    如果我们需要使用装饰器,只需要在被测试的函数前加上@装饰器函数名
    @timmer 等效于 test=timmer(test)
    @timer  #@timer就等同于cal=timer(cal)
    def cal(array):
        res=0
        for i in array:
            res+=i
        return res
    
    cal(range(10))

    补充

    a,b,c=(1,2,3)
    print(a)
    print(b)
    print(c)
    a,b,c='hel'
    print(a)
    print(b)
    print(c)
    
    #如果我们不使用索引,如何去列表的第一个值与最后一个值
    list=[10,4,2,7,2,8,3,8,3,6]
    a,*d,c=list#这里*代表中间所有的值,*后面的代表为一个变量d接收中间的所有值
    print(a)
    print(c)
    print(d)
    
    #使用索引取第一个和最后一个值
    a,c=list[0],list[-1]
    print(a)
    print(c)
    
    #在python中交换两个值
    f1=1
    f2=2
    f1,f2=f2,f1
    print(f1,f2)
    解压序列

    装饰器应用示例

    user_list=[
        {'name':'alex','passwd':'123'},
        {'name':'linhaifeng','passwd':'123'},
        {'name':'wupeiqi','passwd':'123'},
        {'name':'yuanhao','passwd':'123'},
    ]
    
    current_user={'username':None,'login':False}
    
    def auth_deco(func):
        def wrapper(*args,**kwargs):
            if current_user['username'] and current_user['login']:
                res=func(*args,**kwargs)
                return res
            username=input('用户名: ').strip()
            passwd=input('密码: ').strip()
    
            for index,user_dic in enumerate(user_list):
                if username == user_dic['name'] and passwd == user_dic['passwd']:
                    current_user['username']=username
    
                    current_user['login']=True
                    res=func(*args,**kwargs)
                    return res
                    break
            else:
                print('用户名或者密码错误,重新登录')
    
        return wrapper
    
    @auth_deco
    def index():
        print('欢迎来到主页面')
    
    @auth_deco
    def home():
        print('这里是你家')
    
    def shopping_car():
        print('查看购物车啊亲')
    
    def order():
        print('查看订单啊亲')
    
    print(user_list)
    # index()
    print(user_list)
    home()
    
    无参装饰器
    无参装饰器
    user_list=[
        {'name':'alex','passwd':'123'},
        {'name':'linhaifeng','passwd':'123'},
        {'name':'wupeiqi','passwd':'123'},
        {'name':'yuanhao','passwd':'123'},
    ]
    
    current_user={'username':None,'login':False}
    def auth(auth_type='file'):
        def auth_deco(func):
            def wrapper(*args,**kwargs):
                if auth_type == 'file':
                    if current_user['username'] and current_user['login']:
                        res=func(*args,**kwargs)
                        return res
                    username=input('用户名: ').strip()
                    passwd=input('密码: ').strip()
    
                    for index,user_dic in enumerate(user_list):
                        if username == user_dic['name'] and passwd == user_dic['passwd']:
                            current_user['username']=username
                            current_user['login']=True
                            res=func(*args,**kwargs)
                            return res
                            break
                    else:
                        print('用户名或者密码错误,重新登录')
                elif auth_type == 'ldap':
                    print('巴拉巴拉小魔仙')
                    res=func(*args,**kwargs)
                    return res
            return wrapper
        return auth_deco
    
    
    #auth(auth_type='file')就是在运行一个函数,然后返回auth_deco,所以@auth(auth_type='file')
    #就相当于@auth_deco,只不过现在,我们的auth_deco作为一个闭包的应用,外层的包auth给它留了一个auth_type='file'参数
    @auth(auth_type='ldap')
    def index():
        print('欢迎来到主页面')
    
    @auth(auth_type='ldap')
    def home():
        print('这里是你家')
    
    def shopping_car():
        print('查看购物车啊亲')
    
    def order():
        print('查看订单啊亲')
    
    # print(user_list)
    index()
    # print(user_list)
    home()
    
    带参装饰器
    带参装饰器
    import sys,threading,time
    
    
    class KThread(threading.Thread):
    
        """A subclass of threading.Thread, with a kill()
    
        method.
    
    
    
        Come from:
    
        Kill a thread in Python:
    
        http://mail.python.org/pipermail/python-list/2004-May/260937.html
    
        """
    
        def __init__(self, *args, **kwargs):
    
            threading.Thread.__init__(self, *args, **kwargs)
    
            self.killed = False
    
    
    
        def start(self):
    
            """Start the thread."""
    
            self.__run_backup = self.run
    
            self.run = self.__run      # Force the Thread to install our trace.
    
            threading.Thread.start(self)
    
    
    
        def __run(self):
    
            """Hacked run function, which installs the
    
            trace."""
    
            sys.settrace(self.globaltrace)
    
            self.__run_backup()
    
            self.run = self.__run_backup
    
    
    
        def globaltrace(self, frame, why, arg):
    
            if why == 'call':
    
              return self.localtrace
    
            else:
    
              return None
    
    
    
        def localtrace(self, frame, why, arg):
    
            if self.killed:
    
              if why == 'line':
    
                raise SystemExit()
    
            return self.localtrace
    
    
    
        def kill(self):
    
            self.killed = True
    
    
    
    class Timeout(Exception):
    
        """function run timeout"""
    
    
    
    def timeout(seconds):
    
        """超时装饰器,指定超时时间
    
        若被装饰的方法在指定的时间内未返回,则抛出Timeout异常"""
    
        def timeout_decorator(func):
    
            """真正的装饰器"""
    
    
    
            def _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):
    
                result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs))
    
    
    
            def _(*args, **kwargs):
    
                result = []
    
                new_kwargs = { # create new args for _new_func, because we want to get the func return val to result list
    
                    'oldfunc': func,
    
                    'result': result,
    
                    'oldfunc_args': args,
    
                    'oldfunc_kwargs': kwargs
    
                }
    
                thd = KThread(target=_new_func, args=(), kwargs=new_kwargs)
    
                thd.start()
    
                thd.join(seconds)
    
                alive = thd.isAlive()
    
                thd.kill() # kill the child thread
    
                if alive:
    
                    raise Timeout(u'function run too long, timeout %d seconds.' % seconds)
    
                else:
    
                    return result[0]
    
            _.__name__ = func.__name__
    
            _.__doc__ = func.__doc__
    
            return _
    
        return timeout_decorator
    
    
    @timeout(5)
    
    def method_timeout(seconds, text):
    
        print('start', seconds, text)
    
        time.sleep(seconds)
    
        print('finish', seconds, text)
    
        return seconds
    
    
    method_timeout(6,'asdfasdfasdfas')
    超时装饰器

    博文:https://www.cnblogs.com/linhaifeng/articles/6140395.html

  • 相关阅读:
    java基础学习总结——面向对象1
    java基础学习总结——基础语法2
    java基础学习总结——基础语法1
    java基础学习总结——开篇
    java基础学习总结——java环境变量配置
    Java基础加强总结(二)——泛型
    Java基础加强总结(一)——注解(Annotation)
    Web开发中设置快捷键来增强用户体验
    SQLServer2005中的CTE递归查询得到一棵树
    Jquery操作table
  • 原文地址:https://www.cnblogs.com/-wenli/p/10651819.html
Copyright © 2011-2022 走看看