python 函数修饰符@

python 函数修饰符@

@可以使用一个函数(包装函数,wrapper function)替换另一个函数(被包装函数, wrapped function),例如:

def log(func):
    def wrapper_fn():
        print('log开始 ...')
        func()
        print('log结束 ...')
    return wrapper_fn
    #return "abc"  #如果上一句替换成这一句,则最后的test()会返回一个错误:'str' object is not callable

#将wrapper 替换 wrapped, wrapper的返回就是wrapped的值
#这个例子中,由于log函数返回wrapper_fn(一个函数),test被换成wrapper_fn
#如果log函数返回一个字符串,test就是一个字符串了
@log         #引用函数,称为the wrapper,
def test():  #被修饰函数 称为the wrapped
    print('test ..')

test() #实际就是调用了wrapper_fn
print(test.__name__)  #发现输出的是wrapper_fn

输出:

log开始 ...
test ..
log结束 ...
wrapper_fn

可见,test函数被替换了,但是其"__name__"属性也变了。

 

替换以后函数的一些属性改变了,如"__name__",可以使用functools.wraps函数来避免改变:

 

from functools import wraps

def log(func):
    #使用functools.wraps可以使得wrapped 的name属性不变
    @wraps(func, assigned=('__name__',), updated=('__dict__',))  #这里后两个参数需要加逗号,用于表示元组tuple, 否则成字符串了,就不对了。
    def wrapper(*args,**kwargs):
        print('log开始 ...',func.__name__)
        ret = func(*args,**kwargs)
        print('log结束 ...')
        return ret
    return wrapper
    
@log
def test1(s):
    print('test1 ..', s)
    return s

@log
def test2(s1, s2):
    print('test2 ..', s1, s2)
    return s1 + s2


test1('a')
test2('a','bc')

print(test1.__name__)
log开始 ... test1
test1 .. a
log结束 ...
log开始 ... test2
test2 .. a bc
log结束 ...
test1

可见"__name__"属性还是“test1”

当wrapper function 有参数的时候,需要进行两层封装:

from functools import wraps

def log(arg):    #第一层,带一个参数,这个参数就是@的时候的参数
    def _log(func): #第二层,带一个参数,这个参数是wrapped function
        @wraps(func)
        def wrapper(*args,**kwargs):
            print('log开始 ...',func.__name__, arg)            
            ret = func(*args,**kwargs)
            print('log结束 ...')
            return ret
        return wrapper
    return _log
 
@log('module1') #如果wrapper 带有参数,则需要两层定义,如上面的log函数定义。
def test1(s):
    print('test1 ..', s)
    return s

@log('module1')
def test2(s1, s2):
    print('test2 ..', s1, s2)
    return s1 + s2


test1('a')
test2('a','bc')

输出:

log开始 ... test1 module1
test1 .. a
log结束 ...
log开始 ... test2 module1
test2 .. a bc
log结束 ...

总之,@叫做函数修饰符,也可以看做包装一下,用@后面的函数,包装紧接着定义的函数。
这样,以后调用被包装函数时,就替换成@后面的函数了。
这种技术可以使用在调试、测试、记录和额外事务处理中。
当不需要的时候,可直接注释掉@语句。

你可能感兴趣的:(python)