zoukankan      html  css  js  c++  java
  • python学习笔记之---装饰器

    装饰器(decorator):本质上也是函数,接受函数对象来作为参数,并在装饰器的内部来调用接受的函数对象完成相关的函数调用;也是一种设计模式   。
    核心作用:给不同的函数(方法用),可以增加不同的公用的功能。
    装饰器的标志:带@符号的,如:
    @classmethod
    @staticmethod
     
    闭包:函数对象+函数部分所需要使用的一个外部变量=函数
    返回的整体。
     
    示例1:
    #函数对象可以做为返回值返回
    def pri():
        return sum   #函数执行后返回了另外一个函数对象sum
    print(type(pri()))

    D:>py -3 a.py

    <class 'builtin_function_or_method'>
     
    函数对象:就是函数不加()----sum,如果加()就是函数调用----sum()
     
     
     
    示例2:
    #函数的参数可以使用函数对象
    def func1(func):
        return func([1,2,3])
        
    func1(sum)
    
    '''
    func1(sum) #--->6
        #sum->sum([1,2,3])
        #pri-->pri([1,2,3])--->不符合函数定义,会报错
    '''

    执行结果:   

    D:>py -3 a.py
    <class 'builtin_function_or_method'>
    Traceback (most recent call last):
      File "a.py", line 11, in <module>
        func1(pri)
      File "a.py", line 7, in func1
        return func([1,2,3])
    TypeError: pri() takes 0 positional arguments but 1 was given
     
    函数名--->函数对象
    函数名()--->调用函数
     
     
     
     
    #闭包:
    def func():
        x =100
        def func1():
            print(x)
        return func1
     
     
    func()()---等价于
                   a=func()
                   a()
    C:UsersdellDesktop练习6>py -3 0617.py
    100
        #函数名---》返回了函数对象  return func1
        #函数名(参数或无参数)--->调用函数  
        return func1()-->打印100,返回None
    -----》》》分析过程:
    >>> def func():
    ...     x =100
    ...     def func1():
    ...         print(x)
    ...     return func1
    ...
    >>> func()  #加一个括号是调用了func这个函数,执行完后返回的是func1的函数对象;如果想调用func1()打印x的话就得再加一个(),即func()()
    <function func.<locals>.func1 at 0x00000150A43FA620>
    >>> func()()
    100
     
    闭包部分:函数+需要的外部变量一起返回了。闭包必须返回一个函数
    x =100
        def func1():
            print(x)
            return None
        return func1
    func()-->func函数调用后,返回了func1+x
    a=func()--->a=func1,把func1+x赋值给a
    a()---调用func1()--->打印了100,返回了None
     
    #函数对象:就是函数所在的内存地址
     
     
     
     思考:为什么要使用装饰器?什么情况下需要使用装饰器?
    #例:单独的函数,代码都是重复的
    def a():
        time1=time.time()
        i =1
        while i<100000000:
            i+=1
        time2=time.time()
        print("time elapsed:%s" %(time2-time1))
     
    def b():
        time1=time.time()
        i =1
        while i<990000000:
            i+=1
        time2=time.time()
        print("time elapsed:%s" %(time2-time1))
     
     
    #通过函数来尝试解决:
    def timer(func):
        time1=time.time()
        func()  #做了一次函数调用 
        time2=time.time()
        print("time elapsed:%s" %(time2-time1))
     
     
    def a():
        i =1
        while i<100000000:
            i+=1
     
    def b():
        i =1
        while i<990000000:
            i+=1
     
    timer(a)
    timer(b)
     
     
    #加装饰器:给函数增加共有的功能,装饰函数一定要有闭包
    import time
    def timer(func):
        def func1():
            time1=time.time()
            func()  #做一次函数调用
            time2=time.time()
            print("time elapsed:%s" %(time2-time1))
        return func1
        #闭包:func+func1
     
     
    @timer
    def a():
        i =1
        while i<100000000:
            i+=1
     
    a()


    解析: a
    #a等价于timer(a),由python编译器自动完成的。 a() #a()等价于timer(a)()
    本质上相当于a =timer(a)的嵌套调用
     
     
    #装饰器执行过程解析:
    import time
    def timer(func):  #装饰函数
        def wrapper():
            time1=time.time()#定义函数调用前干的事儿
            print("******")
            func()  #做一次函数调用
            time2=time.time()#定义函数调用后干的事儿
            print("******-----------")
            print("time elapsed:%s" %(time2-time1))
        return wrapper
        #闭包:func(闭包变量)+wrapper(闭包函数)
     
    @timer
    def a():
        i =1
        while i<100000000:
            i+=1
        print("a is over!")
     
    def b():
        i =1
        while i<100000000:
            i+=1
     
    #a  #a等价于timer(a),由python编译器自动完成的。
    a()  #a()等价于timer(a)()
     
    b()
    C:UsersdellDesktop练习6>py -3 0617.py
    ******
    a is over!
    ******-----------
    time elapsed:5.6724371910095215
     
     
    a-->timer(a)-->返回wrapper函数对象
    a()-->调用了闭包函数wrapper()
     
    wrapper+a:
            time1=time.time()#定义函数调用前干的事儿
            print("******")
            a()  #做一次函数调用
            time2=time.time()#定义函数调用后干的事儿
            print("******-----------")
            print("time elapsed:%s" %(time2-time1))
     
     
    #有参装饰器
    import time
    def timer(func):  #装饰函数
        def wrapper(arg):
            time1=time.time()#定义函数调用前干的事儿
            print("******")
            func(arg)  #做一次函数调用
            time2=time.time()#定义函数调用后干的事儿
            print("******-----------")
            print("time elapsed:%s" %(time2-time1))
        return wrapper
        #闭包:func(闭包变量)+wrapper(函数)
     
    @timer
    def a(count):
        print("执行次数:",count)
        i =1
        while i<count:
            i+=1
        print("a is over!")
     
    a(20)
    C:UsersdellDesktop练习6>py -3 0617.py
    ******
    执行次数: 20
    a is over!
    ******-----------
    time elapsed:0.0013477802276611328
     
     
     
    #多个参数的装饰器:可变参数
    import time
    def timer(func):  #装饰函数
        def wrapper(*arg,**kw):
            time1=time.time()#定义函数调用前干的事儿
            print("******")
            func(*arg,**kw)  #做一次函数调用
            time2=time.time()#定义函数调用后干的事儿
            print("******-----------")
            print("time elapsed:%s" %(time2-time1))
        return wrapper
        #闭包:func(闭包变量)+wrapper(函数)
     
    @timer
    def a(count):
        print("执行次数:",count)
        i =1
        while i<count:
            i+=1
        print("a is over!")
     
    @timer
    def b(count1,count2,count3):
        print("执行次数:",count1+count2+count3)
     
    a(100)
    b(200,300,count3=1000)
    C:UsersdellDesktop练习6>py -3 0617.py
    ******
    执行次数: 100
    a is over!
    ******-----------
    time elapsed:0.0009958744049072266
    ******
    执行次数: 1500
    ******-----------
    time elapsed:0.0
     
     
     
     
    总结:
    闭包:执行时需要的外部变量+函数对象
     
    装饰器:
    定义一个装饰函数,函数必须返回一个闭包函数,
    并且func(被装饰的函数)会被python自动传递给
    装饰函数,作为装饰函数的一个参数。
     
    装饰函数中的闭包函数结构如下:
     
    def wrapper(*arg,**kw):
        xxxxx干一些事情
        func(*arg,**kw)  #执行被装饰函数
        yyy   干另外一些事情   
     
    使用装饰器,需要在其他函数的上面写个@xxxxx
     
     
     

    内置装饰器

    Python中内置的装饰器有有三个:
    staticmethod:定义实例方法为静态方法
    classmethod:定义实例方法为类方法
    property:对类属性的操作

     
     
     

    装饰器顺序
    但同时对一个函数使用多个不同的装饰器进行装饰时, 这个时候装饰器的顺序就很重要了。
    代码示例:
    @A
    @B
    @C
    def f():
        pass
    等价于: f = A(B(C(f)))
    #先执行C,B,A的前半部分,再执行C,B,A的后半部分
     示例:
    from time import ctime
    
    def deco1(func):
        def decorator1(*args, **kwargs):
            print('[%s]  %s() is called' % (ctime(), func.__name__))
            return func(*args, **kwargs)
        return decorator1
    
    
    def deco2(func):
        def decorator2(*args, **kwargs):
            print('[%s]  %s() is called' % (ctime(), func.__name__))
            return func(*args, **kwargs)
        return decorator2
    
    @deco2
    @deco1
    def foo():
        print('Hello, Python')
    
    foo()

    运行结果:

    E:pytest202001>py -3 a0109.py
    [Thu Jan 9 10:12:45 2020] decorator1() is called
    [Thu Jan 9 10:12:45 2020] foo() is called
    Hello, Python

     
     
     

    装饰器九步法:
    第一步:最简单的函数,准备附加额外功能

    # -*- coding:utf-8 -*-
    '''示例1: 最简单的函数,表示调用了两次'''
    def myfunc():
        print ("myfunc() called.")

    myfunc()
    myfunc()

    E:>py -3 a.py
    myfunc() called.
    myfunc() called.

    第二步:使用装饰函数在函数执行前和执行后分别附加额外功能

    '''
    示例2: 替换函数(装饰)装饰函数的参数是被装饰的函数对象,返回原函数对象 装饰的实质语句: myfunc = deco(myfunc)
    ''' def deco(func): print ("before myfunc() called.") func() print (" after myfunc() called.") return func def myfunc(): print (" myfunc() called.") myfunc = deco(myfunc) myfunc() myfunc()

    E:>py -3 a.py
    before myfunc() called.
    myfunc() called.
    after myfunc() called.
    myfunc() called.
    myfunc() called.

    第三步:

    '''示例3: 使用语法@来装饰函数,相当于“myfunc = deco(myfunc)”
    但发现新函数只在第一次被调用,且原函数多调用了一次'''
    def deco(func):
        print ("before myfunc() called.")
        func()
        print (" after myfunc() called.")
        return func
    @deco  #等价于:deco(myfunc)
    def myfunc():
        print (" myfunc() called.")
    
    myfunc()
    myfunc()

    E:>py -3 a.py
    before myfunc() called.
    myfunc() called.
    after myfunc() called.
    myfunc() called.
    myfunc() called.

    ------->>>>>>

    执行逻辑:
    deco(myfunc)
    print ("before myfunc() called.")
    myfunc()
    print ("  after myfunc() called.")
    print (" myfunc() called.")
    print (" myfunc() called.")
     
     

    第四步:使用内嵌包装函数来确保每次新函数都被调用

    装饰器的规则:
    规则1:
    函数func上面定义了@xxxx,那么等价于 func = xxxx(func)
    规则2:
    装饰函数xxxx,必须返回一个闭包(一个内置函数+func)
    '''示例4: 使用内嵌包装函数来确保每次新函数都被调用,
    内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装
    函数对象'''
    def deco(func):
        def _deco():
            print ("before myfunc() called.")
            func()
            print (" after myfunc() called.")
            # 不需要返回func,实际上应返回原函数的返回值
        return _deco
    
    @deco
    def myfunc():
        print (" myfunc() called.")
        return 'ok'
    
    myfunc() #---->执行闭包函数_deco()
    myfunc() #---->执行闭包函数_deco()

    #myfunc=deco(myfunc)----->返回一个闭包:_deco的函数+myfunc

    E:>py -3 a.py
    before myfunc() called.
    myfunc() called.
    after myfunc() called.
    before myfunc() called.
    myfunc() called.
    after myfunc() called.

    分析:
    def deco(func):
        def _deco():
            print ("before myfunc() called.")
            func()
            print ("  after myfunc() called.")
            # 不需要返回func,实际上应返回原函数的返回值
        return _deco
    闭包:_deco+func
     
    @deco   
    def myfunc():
        print (" myfunc() called.")
        return 'ok'
     
    myfunc=deco(myfunc)
    myfunc是什么?是闭包:_deco+myfunc
    myfunc()--->_deco()
    myfunc()--->_deco()
     
    def outer(name):
         def inner():
             print(name)
         return inner
    闭包:inner+name
    执行过程解释:
    装饰函数deco
    被装饰函数myfunc
    @deco   
    def myfunc():   --->myfunc= deco(myfunc)
        myfunc= deco(myfunc)干了什么呢?
                    1 调用了deco(myfunc)
                    2  返回闭包:_deco+外包变量myfunc
                    3 闭包赋值给了myfunc
                    4 提醒myfunc变为了闭包函数对象
      
    myfunc()---》干了什么呢?
             1 _deco()执行了
            2 print ("before myfunc() called.")
            3 myfunc()
            4 print ("  after myfunc() called.")
     
    myfunc()---》干了什么呢?
             1 _deco()执行了
            2 print ("before myfunc() called.")
            3 myfunc()
            4 print ("  after myfunc() called.")

    第五步:对带参数的函数进行装饰

    '''示例5: 对带参数的函数进行装饰,
    内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装
    函数对象'''
    def deco(func):
        def _deco(a, b):
            print ("before myfunc() called.")
            ret = func(a, b)
            print (" after myfunc() called. result: %s" % ret)
            return ret
        return _deco
    
    @deco
    def myfunc(a, b):
        print (" myfunc(%s,%s) called." % (a, b))
        return a + b
    
    myfunc(1, 2)
    myfunc(3, 4)

    E:>py -3 a.py
    before myfunc() called.
    myfunc(1,2) called.
    after myfunc() called. result: 3
    before myfunc() called.
    myfunc(3,4) called.
    after myfunc() called. result: 7

      

    第六步:对参数数量不确定的函数进行装饰

    '''示例6: 对参数数量不确定的函数进行装饰,
    参数用(*args, **kwargs),自动适应变参和命名参数'''
    def deco(func):
        def _deco(*args, **kwargs):
            print ("before %s called." % func.__name__)
            ret = func(*args, **kwargs)
            print (" after %s called. result: %s" % (func.__name__, ret))
            return ret
        return _deco
    
    @deco
    def myfunc(a, b):
        print (" myfunc(%s,%s) called." % (a, b))
        return a+b
    
    @deco
    def myfunc2(a, b, c):
        print (" myfunc2(%s,%s,%s) called." % (a, b, c))
        return a+b+c
    
    myfunc(1, 2)
    myfunc(3, 4)
    myfunc2(1, 2, 3)
    myfunc2(3, 4, 5)

    E:>py -3 a.py
    before myfunc called.
    myfunc(1,2) called.
    after myfunc called. result: 3
    before myfunc called.
    myfunc(3,4) called.
    after myfunc called. result: 7
    before myfunc2 called.
    myfunc2(1,2,3) called.
    after myfunc2 called. result: 6
    before myfunc2 called.
    myfunc2(3,4,5) called.
    after myfunc2 called. result: 12

    第七步:被装饰函数加参数:带参数的装饰器本质都是两层闭包

    '''示例7: 在示例4的基础上,让装饰器带参数,
    和上一示例相比在外层多了一层包装。
    装饰函数名实际上应更有意义些'''
    def deco(arg):
        def _deco(func):
            def __deco():
                print ("before %s called [%s]." % (func.__name__, arg))
                func()
                print (" after %s called [%s]." % (func.__name__, arg))
            return __deco
        return _deco
    
    @deco("mymodule")
    def myfunc():
        print (" myfunc() called.")
    
    @deco("module2")
    def myfunc2():
        print (" myfunc2() called.")
    
    myfunc()
    myfunc2()

    '''
    1)多了一步:deco("hello") ---》返回了闭包:__deco+s
    deco=闭包:__deco+s
    2)@deco--->__deco(func)+s--->返回了一个闭包_deco+func+s
    后面的过程跟上一步的过程一样。
    '''

    E:>py -3 a.py
    before myfunc called [mymodule].
    myfunc() called.
    after myfunc called [mymodule].
    before myfunc2 called [module2].
    myfunc2() called.
    after myfunc2 called [module2].

    第八步:让装饰器带 类 参数

    '''示例8: 装饰器带类参数'''
    class locker:
        def __init__(self):
            print ("locker.__init__() should be notcalled.")
    
        @staticmethod
        def acquire():
            print ("locker.acquire() called.(这是静态方法)")
    
        @staticmethod
        def release():
            print (" locker.release() called.(不需要对象实例)")
    def deco(cls): '''cls 必须实现acquire和release静态方法''' def _deco(func): def __deco(): print("before %s called [%s]." %(func.__name__, cls)) cls.acquire() try: return func() finally: cls.release() return __deco return _deco @deco(locker) def myfunc(): print (" myfunc() called.") myfunc() myfunc()

    E:>py -3 a.py
    before myfunc called [<class '__main__.locker'>].
    locker.acquire() called.(这是静态方法)
    myfunc() called.
    locker.release() called.(不需要对象实例)
    before myfunc called [<class '__main__.locker'>].
    locker.acquire() called.(这是静态方法)
    myfunc() called.
    locker.release() called.(不需要对象实例)

    第九步:装饰器带类参数,并分拆公共类到其他py文件中,同时演示了对一个函数应用多个装饰器

    '''mylocker.py: 公共类 for 示例9.py'''
    class mylocker:
        def __init__(self):
            print("mylocker.__init__() called.")
        @staticmethod
        def acquire():
            print("mylocker.acquire() called.")
        @staticmethod
        def unlock():
            print(" mylocker.unlock() called.")
    
    class lockerex(mylocker):
        @staticmethod
        def acquire():
            print("lockerex.acquire() called.")
        @staticmethod
        def unlock():
            print(" lockerex.unlock() called.")
    
    
    def lockhelper(cls):
        '''cls 必须实现acquire和release静态方法'''
        def _deco(func):
            def __deco(*args, **kwargs):
                print("before %s called." %func.__name__)
                cls.acquire()
                try:
                    return func(*args, **kwargs)
                finally:
                    cls.unlock()
            return __deco
        return _deco
    '''示例9: 装饰器带类参数,并分拆公共
    类到其他py文件中
    同时演示了对一个函数应用多个装饰
    器'''
    from mylocker import *
    class example:
        @lockhelper(mylocker)
        def myfunc(self):
            print (" myfunc() called.")
        @lockhelper(mylocker)
        @lockhelper(lockerex)
        def myfunc2(self, a, b):
            print (" myfunc2() called.")
            return a + b
    
    if __name__=="__main__":
        a = example()
        a.myfunc()
        print(a.myfunc())
        print (a.myfunc2(1, 2))
        print (a.myfunc2(3, 4))
  • 相关阅读:
    SpringCloud之Eureka注册中心
    微服务入门概念
    迁移桌面程序到MS Store(3)——开机自启动
    迁移桌面程序到MS Store(2)——Desktop App Converter
    迁移桌面程序到MS Store(1)——通过Visual Studio创建Packaging工程
    [UWP]涨姿势UWP源码——适配电脑和手机
    [UWP]涨姿势UWP源码——UI布局
    微软2017MVP大礼包拆箱攻略
    [UWP]涨姿势UWP源码——Unit Test
    [UWP]涨姿势UWP源码——IsolatedStorage
  • 原文地址:https://www.cnblogs.com/wenm1128/p/11716550.html
Copyright © 2011-2022 走看看