美文网首页Python新世界
Python装饰器是一款神奇的神器!你知道怎么用吗?一文搞懂它!

Python装饰器是一款神奇的神器!你知道怎么用吗?一文搞懂它!

作者: 919b0c54458f | 来源:发表于2018-11-23 21:00 被阅读5次

    进群:548377875  即可获取小编精心准备的教程以及大量的PDF呢!

    1.引子

    #功能函数

    def add(x,y):

    return x+y

    #装饰函数

    def logger(fn):

    print('frist')

    x = fn(4,5)

    print('second')

    return x

    print(logger(add))

    #把函数add传给logger ,return x+y

    #print('frist')

    #print('secend')

    # x = fn(4,5) ==> x = 4 y= 5 x= 4+5 = 9

    #return 9

    frist

    second

    9

    2.提取参数

    x,y的参数都放在logger函数内部了,影响函数的灵活性,此处我们可以提取出来。

    def add(x,y):

    return x + y

    def logger(fn,*args,**kwargs):

    print('frist')

    x = fn(*args,**kwargs)

    print('second')

    return x

    print(logger(add,1,y=11))

    frist

    second

    12

    3.柯里化

    def add(x,y):

    return x + y

    def logger(fn):

    def wrapper(*args,**kwargs):

    print('begin')

    x = fn(*args,**kwargs)

    print('end')

    return x

    return wrapper

    print(logger(add)(5,y=11))

    begin

    end

    16

    懵逼ing

    以下为个人理解,左边为非柯里化函数,右边是柯里化函数。

    柯里化函数

    前面说过柯里化的定义,本来可以一次传入两个参数,柯里化之后。只需要传入一个函数了。。

    左边传入add 和 两个参数。

    右边的logger(add)是一个函数,只需要传入两个参数。logger(add)是个整体,结合成一个函数。当然这样写,我们看函数主题的部分也是不一样的。

    函数的基础中说过,函数的传参必须和函数参数的定义一致。重点分析右边函数(柯里化)。

    参数部分:参数传入的方式,logger函数需要传入个fn,fu的返回值是wrapper函数,wrapper函数的参数是(*args,**kwargs)所以此次就需要分两次传入参数。

    第一次传入fn,再次传入wrapper函数需要的参数。所以就出现了最下边的调用方式。

    print(logger(add)(5,y=50))。

    返回值部分:右侧的logger函数是个嵌套函数,logger的返回值是wrapper,内层的wrapper函数返回值是x,x = fn(*args,**kwargs)。fn函数是最后调用时候传入的add函数。

    懵逼 X 2。。。。

    def add(x,y):

    return x + y

    def logger(fn,*args,**kwargs): def logger(fn): #参数剥离

    def newfunction(*args,**kwargs): #新定义一个函数,logger函数返回也是这个函数名字

    print('frist') print('frist')

    x = fn(*args,**kwargs) == > x = fn(*args,**kwargs)

    print('second') print('second')

    return x return x

    return newfunction

    print(logger(add,1,y=11)) print(logger(add)(5,y=11)) #两次传入参数

    效果如下:

    def add(x,y):

    return x + y

    def logger(fn): #参数剥离

    def newfunction(*args,**kwargs): #新定义一个函数,logger函数返回也是这个函数名字

    print('frist')

    x = fn(*args,**kwargs)

    print('second')

    return x

    return newfunction

    print(logger(add)(5,y=11)) #两次传入参数

    frist

    second

    16

    继续懵逼的话就这样用吧。。。用多了就悟道了。。

    4.装饰器语法糖

    #再次变形。。。

    def add(x,y):

    return x + y

    def logger(fn):

    def wrapper(*args,**kwargs):

    print('begin')

    x = fn(*args,**kwargs)

    print('end')

    return x

    return wrapper

    ##调用方法1:

    print(logger(add)(x=1111,y=1))

    ##调用方法2:

    add = logger(add)

    print(add(x=11,y=3))

    ##调用方法3: python给我们的语法糖

    @logger # 说明下边的函数,add 其实是 add = logger(add)

    def add(x,y):

    return x + y

    print(add(45,40))

    begin

    end

    1112

    begin

    end

    14

    begin

    end

    85

    三.复杂的栗子

    import datetime

    import time

    def logger(fn):

    def warp(*arges,**kwarges):

    print("arges={},kwarges={}".format(arges,kwarges)) #打印函数的两个参数

    start = datetime.datetime.now() #获取函数运行的开始时间

    ret = fn(*arges,**kwarges) #传入两个参数,调用add函数 此处有个return的值,需要一层一层的返回出去

    duratime = datetime.datetime.now() - start #获得函数的运行时间

    print("function {} took {}s".format(fn.__name__,duratime.total_seconds())) #打印函数的运行时间

    return ret #返回fn的结果 ,fn = x+y ==> 返回x+y的值。 x = 4 y= 11 ==> return 11

    return warp #返回warp的 return ==> ret 的return ==> return 11 函数的最终结果为11

    @logger

    def add(x,y):

    print("oooooook")

    time.sleep(1.5)

    return x+y

    print(add(4,y=11))

    #如果充分理解了每个小部件,这个简单的完整版本也是很好理解的了。

    #1,logger是个装饰器,而且使用了柯里化技术

    #2,add 传参给logger的fn 形参,add(4,y=5)的两个参数传入给warp函数的两个形参

    #

    #

    arges=(4,),kwarges={'y': 11}

    oooooook

    function add took 1.5017s

    15

    再次翻译

    import datetime

    import time

    #####################################装饰开始############################################

    def logger(fn): #拿到函数名称

    def warp(*arges,**kwarges): #拿到函数带过来的参数开始装饰

    print("arges={},kwarges={}".format(arges,kwarges)) #来试试打印两个参数

    start = datetime.datetime.now() #

    ret = fn(*arges,**kwarges) # 此处调用add函数。开始执行函数,发现return语句。。ret的结果就是return。

    duratime = datetime.datetime.now() - start #

    print("function {} took {}s".format(fn.__name__,duratime.total_seconds()))

    return ret #加工完成开始返回。warp的返回值是ret ,ret的返回值是 add函数的执行结果(原函数的功能完整的保留了)

    return warp # logger的返回结果是warp,warp的返回值是ret ,ret的返回值是 add函数的执行结果(原函数的功能完整的保留了)

    #####################################装饰完成############################################

    @logger #装饰工厂

    ######add是需要被装饰的函数,当你有这个想法的事情,其实事情已经开始发生了。

    def add(x,y): # 此时add = logger(add) 此处前面的@logger标记就是想要让logger装饰器像一个工厂一样对add函数进行加工。

    print("oooooook")

    time.sleep(1.5)

    return x+y

    print(add(4,y=11))

    arges=(4,),kwarges={'y': 11}

    oooooook

    function add took 1.501604s

    15

    四.带参装饰器

    1. 文档字符串

    我们约定,在python函数的第一行需要对函数进行说明,使用三引号表示。

    如果是英文说明,惯例首字母大写,第一行写概述,空一行,第三行写详细描述。

    如果函数中有文档字符串,默认会放在函数的doc属性中,可以直接访问。

    def add(x,y):

    """This is a function of addition"""

    a = x+y

    return x + y

    print("function name is {}

    function doc = {}

    ".format(add.__name__, add.__doc__))

    print(help(add))

    function name is add

    function doc = This is a function of addition

    Help on function add in module __main__:

    add(x, y)

    This is a function of addition

    None

    2. 前面装饰器的副作用

    前面装饰器基本上已经可以完成对函数进行加强的功能了,但是还有些瑕疵。比如原来函数的原属性已经被替换为装饰器的属性了。如下:

    def add(x,y):

    return x + y

    def logger(fn):

    "This is logger doc"

    def wrapper(*args,**kwargs):

    "This is wrapper doc"

    print('begin')

    x = fn(*args,**kwargs)

    print('end')

    return x

    return wrapper

    @logger # add = logger(add)

    def add(x,y):

    "This is add doc "

    print("name = {}

    doc = {}".format(add.__name__,add.__doc__))

    return x + y

    print(add(45,40))

    #可以看出来add被装饰出来的函数(新的add)的属性已经全部改变了。

    begin

    name = wrapper

    doc = This is wrapper doc

    end

    85

    3. 解决方案一

    三个函数:

    第一个:copy原函数的属性 copy_properties

    第二个:装饰器 logger

    第三个:功能函数 add

    def copy_properties(src, dst): # 把src的相关属性赋值给dst (fn,wrap)

    dst.__name__ = src.__name__

    dst.__doc__ = src.__doc__

    def logger(fn):

    """'This is a function of logger'"""

    def wrap(*arges,**kwarges): #

    """'This is a function of wrap'"""

    print('<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>')

    x = fn(*arges,**kwarges)

    #print("name={}

    doc={}".format(add.__name__,add.__doc__))

    print('<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>')

    return x

    copy_properties(fn,wrap) #思考1:为什么放在这个位置调用

    return wrap

    @logger

    def add(x,y):

    """'This is a function of add'"""

    print("name={}

    doc={}".format(add.__name__,add.__doc__))

    return x+y

    print(add(4,6))

    <>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>

    name=add

    doc='This is a function of add'

    <>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>-<>

    10

    4. 解决方案二

    但凡使用装饰器都会出现属性的这个问题,为什么不把copy_properties也做成装饰器呢?

    三个函数:

    第一个:copy原函数的装饰器 copy_properties1

    第二个:装饰器 logger

    第三个:功能函数 add

    def copy_properties(src, dst): # 把src的相关属性赋值给dst (fn,wrap)

    dst.__name__ = src.__name__

    dst.__doc__ = src.__doc__

    #利用前面的知识我们可以对copy_properties轻松进行变形

    def copy_properties1(src): # 把src的相关属性赋值给dst (fn,wrap)

    def _copy(dst):

    dst.__name__ = src.__name__

    dst.__doc__ = src.__doc__

    return dst

    return _copy

    带参装饰器:

    def logger(fn):

    """'This is a function of logger'"""

    @copy_properties1(fn) #wrap = copy_properties(fn)(wrap)

    #== > 柯里化 两次传入参数 src = fn , dst = wrap 新的wrap函数的属性已经替换为原函数的。

    def wrap(*arges,**kwarges): #wrap = copy_properties(fn)(wrap)(*arges,**kwarges)

    """'This is a function of wrap'"""

    print('>->->->->->->->->->->->->->->->->->->->->->->->->->')

    x = fn(*arges,**kwarges)

    print('<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<-<')

    return x

    return wrap

    @logger #add =logger(add)

    def add(x,y):

    """'This is a function of add'"""

    print("name={}

    doc={}".format(add.__name__,add.__doc__))

    return x+y

    print(add(4,11))

    相关文章

      网友评论

        本文标题:Python装饰器是一款神奇的神器!你知道怎么用吗?一文搞懂它!

        本文链接:https://www.haomeiwen.com/subject/wvysqqtx.html