2020.7.2 装饰器 偏函数

装饰器Decorator

含义:在代码运行期间动态增加功能的方式

实质:返回函数的高阶函数(即参数中含有函数,且返回一个函数)

用法:要用函数前用Python的 语法标注Decorator

例一:

一个打印日志的Decorator

定义:

def log(func):
    def wrapper(*args,**kw): #1
        print('call %s()'%func.__name__) #每个函数都有一个__name__属性,表示函数名
        return func(*args,**kw) #2
    return wrapper

 

这就是定义一个Decorator的基本格式

即:

def log(func):
    def wrapper(*args,**kw): #1
        #操作
        return func(*args,**kw) #2
    return wrapper

#2处不能删去,表示Decorator运行后要执行将要执行的函数,没有这句话,就会只执行log,而不执行后续函数

 

运行:

@log #1
def now():
    print('2020.7.2')

now()

#1处log在now()前,表示 log是函数now()的Decorator,在执行now()前先运行log

 

结果:

call now()
2020.7.2

 

如果不要#2,即wrapper中的return func(*args , **kw)时,结果:

call now()

只运行了log,而没有运行函数now

说明:Decorator是和后续引用的函数捆绑的,必须在Decorator中显式表明执行后续函数,即#2

def wrapper(*args ,  **kw)
    #操作
    return func(*args,**kw)

 

@log放在now()前,相当于执行了语句

now=log(now)

由于函数名变量,所以@log后的now函数,调用后不再是之前的now函数了,而是log中返回的那个函数,即wrapper,这也可以解释为什么没有#2就无法执行原来now()中的语句了

 

三层嵌套装饰器Decorator:装饰器本身需要传入参数,类似 @log('execute') 这样

方法:再加一层返回Decorator的高阶函数

def log( text ):
    def decorator(func):
        def wrapper(*args , **kw):
            print('%s  call%s()'%(text,func.__name__))
            return func(*args , **kw)
        return wrapper
    return decorator

用法:

@log( 'execute' )
def num():
    print ('2020.7.2')

结果:

execute call num()
2020.7.2

 

相当于:

now=log( ' execute' )(num)

 

这样之后,now不代表原来的now()函数,而是wrapper函数了,可以用now.__name__查看

now.__name__
'wrapper'

 

通常不希望函数名的变化,这样的话可以避免一些依赖函数签名的代码出错

方法:Python内置的functools.wraps可以保证函数名decorator前后不变

用法:在内部的wrapper函数前加一句@functools.wraps(func),这个参数func是Decorator后的函数名,使用前需要先import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        #……
        return func(*args,**kw)
    return wrapper

三层嵌套的Decorator,仍然是在wrapper前加这句话

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args,**kw):
            #……
            return func(*args,**kw)
        return wrapper
    return decorator

这时再用name查看,则会发现name在使用Decorator前后没有改变

now.__name__
'now'

 

例二:一个既支持@log(),又支持@log(text)的Decorator的实现

方法:使用默认参数text=None

def log(text=None):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            if text: #1
                print("%s call %s()" % (text, func.__name__))
            else:
                print("call %s()" % func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

if判断语句用在wrapper下,如#1

 

偏函数

含义:通过functools.partial根据某个已存在的函数,通过固定该函数的某个参数,创建一个新函数

举例:由int()得到2进制int2()函数,该函数的作用是将 字符串形式二进制整数化为十进制整数

import functools
int2=functools.partial( int , base = 2 )

int2('10000')
16

如果输入的字符串不是二进制,如 ' 1234 ',在用int2时就会出错

 

补充:partial可接收三个参数,1、基函数   2、*args   3、**kw

其中

1、基函数:是基于该函数创建的偏函数

2、*args:多出来一部分数字参与运算

3、**kw:需要固定的基函数的参数,格式为 xxx=x

所以,int2('10000')等价于

kw={'base' : 2}
int('10000',**kw)

 

另有一个关于*args的例子:

max2=functools.partial(max,10)

表示max2是由max函数创建的,10表示每次用max2函数,都要额外加入一个数字10参与运算

max2(5,6,7)
#等价于
args=(10,5,6,7)
max(*args)

 

posted @ 2020-07-05 12:37  ShineLe  阅读(23)  评论(0)    收藏  举报