python - 装饰器

循序渐进的方式介绍(或直接跳到第8步看模板):

 

1. 函数基础理解

def hi(name='zhangyang'):   # 函数定义
    return 'hi ' + name


print(hi())                 # 函数调用,打印:hi zhangyang


# 将一个hi函数,赋值给一个greet变量(函数后跟小括号:函数调用;函数后不跟小括号:赋值)
greet = hi

print(greet)   # <function hi at 0x0000020E4C43B400>
print(hi)      # <function hi at 0x0000020E4C43B400>

print(greet())    # 打印:hi zhangyang
print(hi())       # 打印:hi zhangyang


# 删除hi函数
del hi
print(hi())      # 报错,打印:NameError: name 'hi' is not defined
print(greet())   # 打印:hi zhangyang

 

2. 在一个函数中,定义另外一个函数 

def hi(name='zhangyang'):    # 函数定义
    print('now you are in the hi() function')

    def greet():   # 在函数中定义的函数
        return 'now you are in the greet() function'

    def welcome():
        return 'now you are in the welcome() function'

    print(greet())    # 调用greet()函数,并打印
    print(welcome())
    print('now you are back in the hi() function')


hi()    # 函数调用,打印如下:
'''
now you are in the hi() function
now you are in the greet() function
now you are in the welcome() function
now you are back in the hi() function
'''
greet()   # greet()函数在hi()函数体外,不能被访问,会报错。打印:NameError: name 'greet' is not defined

 

3. 从函数中返回函数

def hi(name='zhangyang'):
    def greet():
        return 'now you are in the greet() function'

    def welcome():
        return 'now you are in the welcome() function'

    if name == 'zhangyang':
        return greet   # 不带小括号,可以被传递(赋值给别的变量,而不去执行它)
    else:
        return welcome


a = hi()         # a指向hi()函数中的greet()函数
print(a)         # 打印:<function hi.<locals>.greet at 0x0000028001FFB840>

a = hi('lisi')   # a指向hi()函数中的welcome()函数
print(a)         # 打印:<function hi.<locals>.welcome at 0x0000028AA4D2BAE8>

print(a())       # 打印:now you are in the greet() function
print(hi()())    # 打印:now you are in the greet() function

 

4. 将函数作为参数,传递给另外一个函数 

def hi():
    return 'hi zhangyang'


def doSomethingBeforeHi(func):   # 函数定义:将另一个函数作为形参
    print('i an doing some work before executing hi()')
    print(func())


doSomethingBeforeHi(hi)  # 函数调用,将hi()函数传入(执行了doSomethingBeforeHi(),也执行了它中的hi()),打印如下:
'''
i an doing some work before executing hi()
hi zhangyang
'''

 

5. 创建一个装饰器

def a_new_decorator(a_func):
    def wrap_the_function():   # 定义wrap_the_function()
        print('i am doing some work before executing a_func()')
        a_func()
        print('i am doing some work after executing a_func()')

    return wrap_the_function   # 返回wrap_the_function()


def a_function_requiring_decorator():    # 函数定义
    print('i am the function which needs some decoration')


# 1. 普通函数调用
a_function_requiring_decorator()   # ***普通函数调用,打印如下:
'''
i am the function which needs some decoration
'''


# 2. 装饰后的函数调用
a_function_requiring_decorator = a_new_decorator(a_function_requiring_decorator)
print(a_function_requiring_decorator)   # 打印:<function a_new_decorator.<locals>.wrap_the_function at 0x000002434CA3B7B8>
a_function_requiring_decorator()  # ***装饰后的函数调用,调用a_new_decorator()中的wrap_the_function(),打印如下:
'''
i am doing some work before executing a_func()
i am the function which needs some decoration
i am doing some work after executing a_func()
'''

 

6. 使用@符号创建一个装饰器 

def a_new_decorator(a_func):
    def wrap_the_function():   # 定义wrap_the_function()
        print('i am doing some work before executing a_func()')
        a_func()
        print('i am doing some work after executing a_func()')

    return wrap_the_function   # 返回wrap_the_function()


@a_new_decorator    # 使用@符号,简洁的方式生成被装饰的函数
def a_function_requiring_decoration():
    print('i am the function which needs some decoration')


a_function_requiring_decoration()  # 函数调用,执行的是被装饰后的函数,打印如下:
'''
i am doing some work before executing a_func()
i am the function which needs some decoration
i am doing some work after executing a_func()
'''

ps:使用@符号和第5步中的下面一句,作用相同(使用@符号更简洁):

a_function_requiring_decorator = a_new_decorator(a_function_requiring_decorator)

 

7. wraps

第6步中,如果直接打印被装饰函数的名称,打印不出来(函数的名字和docstring被重写了),如下:

print(a_function_requiring_decoration.__name__)   # 打印:wrap_the_function

需要用functools.wraps来解决这个问题,如下:

from functools import wraps   # 导入wraps包


def a_new_decorator(a_func):
    @wraps(a_func)   # 使用wraps处理
    def wrap_the_function():
        print('i am doing some work before executing a_func()')
        a_func()
        print('i am doing some work after executing a_func()')

    return wrap_the_function


@a_new_decorator
def a_function_requiring_decoration():
    print('i am the function which needs some decoration')


a_function_requiring_decoration()
'''
i am doing some work before executing a_func()
i am the function which needs some decoration
i am doing some work after executing a_func()
'''

print(a_function_requiring_decoration.__name__)   # 打印:a_function_requiring_decoration

  

8. 模板

from functools import wraps


def decorator_name(func):   # 装饰函数
    @wraps(func)
    def decorated(*args, **kwargs):
        if not can_run:
            return 'function will not run'
        return func(*args, **kwargs)
    return decorated


@decorator_name
def func():      # 函数定义
    return('function is running')


can_run = True
print(func())    # 函数调用,打印:function is running

can_run = False
print(func())    # 函数调用,打印:function will not run

 

上一篇:jupyter notebook重新导入模块


下一篇:js数组对象过滤:filter,find,some,every