Python学习:函数装饰器

概述

装饰器(decorators)是Python的一个重要的部分,简单的介绍,函数装饰器是修改其他函数的功能的函数,有助于代码更加的简洁,也更有Python范。

函数皆是对象

首先,需要理解在Python中,函数也是对象。比如如下的代码:

def hi(name="yasoob"):
    return "hi " + name

print(hi())
#输出:'hi yasoob'

#可以将一个函数名赋值给一个变量。注意只是函数名func,而不是func()的形式
greet = hi
print(greet())
#输出:'hi yasoob'

#如果删除掉旧的函数名hi,但是不影响新的函数名greet,因为本质上hi与greet只是保存函数地址的变量
#只要函数地址是有效的,则均可以通过变量保存的函数名来调用函数
del hi
print(hi())
#输出:NameError
print(greet())
#输出:'hi yasoob'

在函数中定义函数

在Python中,可以在函数中定义另一个函数,也就是可以创建嵌套的函数:

def hi(name="yasoob"):
    print("now you are inside 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())
    print(welcome())
    print("now you are back in the hi() function")
    
hi()
#调用函数hi,输出结果为:
#'now you are inside 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'

#以上的结果显示了在调用hi()时,函数greet()和welcome()都将被调用
#但是函数greet()和welcome()是在函数hi()内部定义的,在hi()函数外部是不能访问的。
greet()
#输出:NameError: name 'greet' is not defined

从函数中返回函数

在Python中,也可以将一个函数名作为输出返回。

def hi(name="yasoob"):
    def greet():
        return "now you are in the greet() function"
    
    def welcome():
        return "now you are in the welcome() function"
    
    if name == "yasoob":
        return greet
    else:
        return welcome
    
a = hi()
print(a)
#输出:<function greet at 0x7f2143c01500>
#这里的输出显示了变量a保存的是函数内部定义的greet()函数地址,因此a()即可调用greet()函数

print(a())
#输出:'now you are in the greet() function'

注意,在以上代码中,hi函数返回的是 return greet 与 return welcome,而不是 return greet() 与 return welcome(),这里返回的应该是函数名,而不是函数调用,带或者不带括号,是有很大区别的,若返回的是 return greet() ,表示返回的是函数greet执行的结果,而不是返回greet这个函数地址。

将函数作为参数传递给另一个函数

函数的入参可以为普通的变量,也可以为函数名,因为根据以上的知识介绍,函数也是对象,因此函数名也是函数这个对象的引用,如下的实例代码:

def hi():
    return "hi yasoob"

def doSomethingBeforeHi(func):
    print("i am doing some boring work before executing hi()")
    print(func())
    
doSomethingBeforeHi(hi)
#输出:
#'i am doing some boring work before executing hi()'
#'hi yasoob'

函数装饰器

根据以上介绍的知识,可以创建一个函数装饰器了,范例代码如下:

def a_new_decorator(a_func):
    def wrapTheFunction():
        print("i am doing some boring work before executing a_func()")
        a_func()
        print("i am doing some boring work after executing a_func()")
    return wrapTheFunction

def a_function_requiring_decoration():
    print("i am the function which needs some decoration to remove")
    
a_function_requiring_decoration()
#输出:'i am the function which needs some decoration to remove'

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
#这里a_new_decorator函数的入参为函数名a_function_requiring_decoration,
#因此入参为旧的函数a_function_requiring_decoration
#所以在a_new_decorator里定义的wrapTheFunction,将执行a_func(),执行的是旧的函数
#a_new_decorator返回return wrapTheFunction,
#因此变量a_function_requiring_decoration现在指向的是wrapTheFunction函数,而不是原来旧的函数
#注意这里没有print语句被执行到,因为a_new_decorator函数内嵌函数只是定义,不会被执行
#执行的只是return wrapTheFunction这一条语句

a_function_requiring_decoration()
#输出:
#'i am doing some boring work before executing a_func()'
#'i am the function which needs some decoration to remove'
#'i am doing some boring work after executing a_func()'

以上的代码并没有使用到@符号,但是使用@符号只是生成被装饰的函数的简短的方式。原理是一样的。使用@符号生成函数装饰器的代码范例:

@a_new_decorator
def a_function_requiring_decoration():
    print("i am the function which needs some decoration to remove")
    
a_function_requiring_decoration()
#输出:
#'i am doing some boring work before executing a_func()'
#'i am the function which needs some decoration to remove'
#'i am doing some boring work after executing a_func()'

#通过以上的代码可知,@a_new_decorator是以下代码的简短表达方式:
#a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

print(a_function_requiring_decoration.__name__)
#输出:wrapTheFunction
#这里可以看出,变量a_function_requiring_decoration指向的不再是原来的函数,而是内嵌定义的函数
#但是有时候我们希望变量还是指向原来的函数,但是这个函数被内嵌函数的行为重新定义。
#python提供了functools.wraps函数来解决这个问题

from functools import wraps
def a_new_decorator(a_func):
    @wraps(a_func)
    def wrapTheFunction():
        print("i am doing some boring work before executing a_func()")
        a_func()
        print("i am doing some boring work after executing a_func()")
    return wrapTheFunction

@a_new_decorator
def a_function_requiring_decoration():
    print("i am the function which needs some decoration to remove")
    
print(a_function_requiring_decoration.__name__)
#输出:a_function_requiring_decoration
#@wraps接受一个函数进行装饰,并加入了复制函数的名称,注释文档,参数列表等
#这样可以使得在装饰器里面访问在装饰之前的函数的属性

另一范例

使用函数装饰器的另一段代码范例:

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'
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值