[python]返回函数和装饰器

2019-04-08  本文已影响0人  喵吉呀呀
返回函数

返回函数: 函数作为结果返回
e.g:

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

f = lazy_sum(1,2)   # 调用lazy_sum并传参,实质是把sum赋值给f
f()  # 调用
#  这两行相当于lazy_sum(1,2)()
3

>每次调用都会返回一个新的函数,即使传入相同的参数:

f1 = lazy_sum(1, 3, 5, 7, 9)
f2 = lazy_sum(1, 3, 5, 7, 9)
f1==f2
>>>False
装饰器(decorator)

补充知识:
函数对象有一个name属性
e.g:

def now():
    print('2015-3-25')
f = now
now.__name__
>>>'now'
f.__name__
>>>'now'

装饰器写法:
@funcName
e.g:

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

def test():
    print('test')

@print_caller
test()

>>>call test():
>>>test

decorator实质是返回一个函数,所以,原来的test()函数仍然存在,只是现在同名的test变量指向了新的函数,于是调用test()将执行新函数,即在print_caller()函数中返回的wrapper()函数。
wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。
所以,@print_caller相当于执行了:

test = print_caller(test)

打印函数执行前后时间的example:

def metric(func):
    def decorator(*args, **kw):
        print('start:%s' % int(time.time()))
        time.sleep(2)
        r = func(args, **kw)
        print('end:%s' % int(time.time()))
        return r
    return decorator

@metric
def test(a):
    print('test')

test()

>>>start:1554695731
>>>test
>>>end:1554695733

上述几个例子都是decorator不需传参数的情况,如果decorator需要传参数,就需要编写一个返回decorator的高阶函数
e.g:

def add_text(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('text:%s' % text)
            r = func(args, **kw)
            return r
        return wrapper
    return decorator


@add_text('miao')
def test(a):
    print('test')

>>>text:miao
>>>test

实质是三层的嵌套,@add_text相当于执行了:

test = add_text('miao')(test)

*decorator本质的嵌套返回函数,会导致原型指针发生变化,name等属性也随之发生变化,如上述的name会从test变成wrapper,理论上应该添加一句wrapper.name = func.name,把原型指针指针原函数,但Python存在内置的functools.wraps做了这个处理

import functools

# 不带参数的装饰器
def print_caller(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

# 带参数的装饰器
def add_text(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('text:%s' % text)
            r = func(args, **kw)
            return r
        return wrapper
    return decorator

上一篇下一篇

猜你喜欢

热点阅读