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:\Users\dell\Desktop\练习\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)()
 
 
#装饰器执行过程解析:
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:\Users\dell\Desktop\练习\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:\Users\dell\Desktop\练习\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:\Users\dell\Desktop\练习\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
 
 
 

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

# -*- 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))

猜你喜欢

转载自www.cnblogs.com/wenm1128/p/11716550.html