Python中的函数装饰器

先上文档 Function definitions ,如果只是想简单的知道怎么使用函数装饰器,那先看看下面这个例子:

def makebold(fn):
    print("I'm bold")
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

def makeitalic(fn):
    print("I'm italic")
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makebold
@makeitalic
def say():
    return "Hello Python"

print(say())

#Outputs:
# I'm italic
# I'm bold
# <b><i>Hello Python</i></b>

为了理解装饰器,首先必须要知道的一点是在Python中函数也是一种对象,知道了这一点对我们理解后续内容是很有帮助的。下面来看一个例子:

def shout(word="yes"):
    return word.capitalize() + "!"

print(shout())
#outputs: "Yes!"

#前面我们提到函数也是一种对象类型,
#所以你可以像指派其他对象一样把一个函数指派个一个变量,
#注意:在这里我们并没有用圆括号,因为这里并没有调用执行函数调用,仅仅是
# 把函数'shout'指派给了'scream'变量,意味着你可以通过scream调用shout。
scream = shout

print(scream())
#outputs: "Yes!"


print(id(shout))
print(id(scream))
#我们打印来看看两个函数对象的地址:
# 都为 4338464288


#不仅如此,你可以把原来的名字'shout'移除掉,仍然可以调用'scream'对象
del shout
try:
    print(shout())
except NameError as e:
    print(e)
    #outputs: name 'shout' is not defined

print(scream())
#outputs: Yes!

Python函数中一个有趣的属性是我们可以在函数内部定义另一个函数,也就是嵌套:

def talk():
    def whisper(word="yes"):
        return word.lower() + "..."

    #定义函数whisper后就立即调用它
    print(whisper())


#当你每次调用'talk'的时候,函数'whisper'就会被定义,然后紧接着就被调用。
talk()
#outputs: "yes..."


#但是有一点需要知道,'whisper'并不会出现在'talk'外。
try:
    print(whisper())
except NameError as e:
    print(e)
    #outputs: name 'whisper' is not defined

好了,到了这里想必你也该知道其实函数就是一种对象类型:它能被指派给一个变量;能够被定义在另一个函数内部。这也就意味着一个函数能够返回另一个函数:

def getTalk(kind="shout"):

    def shout(word="yes"):
        return word.capitalize() + "!"

    def whisper(word="yes"):
        return word.lower() + "..."

    if kind == "shout":
        #注意:不要在后面加圆括号,这里并没有调用函数,而是返回一个函数对象
        return shout
    else:
        return whisper

talk = getTalk()
#你可以看到talk在这里就是一个函数对象
print(talk)
#outputs: <function getTalk.<locals>.shout at 0x10217b7b8>

#这里我们才调用这个函数对象
print(talk())
#outputs: Yes!

#当然你也可以直接调用:
print(getTalk("whisper")())
#outputs: yes...

你也可以return 一个函数,也可以把一个函数当做参数传递:

def doSomethingBefore(func):
    print("I do something before then I call the function you gave me")
    print(func())

doSomethingBefore(scream)
#outputs: I do something before then I call the function you gave me
#         Yes!

到了这里,你基本就已经具备了链接装饰器的基本条件了装饰器其实就是一个种”wrappers”, 意思就是这种”wrappers”可以让你在它们装饰的函数前后执行一些其他的代码,而不用修改函数本身。

下面我们来手写一个装饰器:

def my_shiny_new_decorator(a_function_to_decorate):

    def the_wrapper_around_the_original_function():

        print("Before the function runs")
        a_function_to_decorate()

        print("After the function runs")

    return the_wrapper_around_the_original_function

def a_stand_alone_function():
    print("I am a stand alone function, don't you dare modify me")

a_stand_alone_function()
#outputs: I am a stand alone function, don't you dare modify me

a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

这里其实就是把一个函数当做一个参数,传递到一个接收一个函数作为参数的嵌套函数内,嵌套函数内调用的才是我们真正传递进去的函数,可以看到在调用该函数前后我们可以执行一些额外的工作。现在,你可能想要在每次调用a_stand_alone_function函数的时候,都调用装饰好的函数a_stand_alone_function_decorated而不是调用装饰前的函数本身。这个也好办,我们只需用my_shiny_new_decorator的返回值来重写a_stand_alone_function就好了。

a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#outputs:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

接下来我们可以用装饰器语法来改写前面的例子:

@my_shiny_new_decorator
def another_stand_alone_function():
    print("Leave me alone")

another_stand_alone_function()  
#outputs:  
#Before the function runs
#Leave me alone
#After the function runs

恩,其实也就是这么简单。@decorator其实就是下面语句的快捷标签:

another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

装饰器其实就是装饰器设计模式 的一种Python化变体。Python中内嵌了很多经典的设计模式,用来使开发变得更容易(比如:iterators)。

当然,你可以一个个的累积装饰器函数:

def bread(func):
    def wrapper():
        print("</''''''\>")
        func()
        print("<\______/>")
    return wrapper

def ingredients(func):
    def wrapper():
        print("#tomatoes#")
        func()
        print("~salad~")
    return wrapper

def sandwich(food="--ham--"):
    print(food)

sandwich()
#outputs: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

也可以使用装饰器语法:

@bread
@ingredients
def sandwich(food="--ham--"):
    print(food)

sandwich()
#outputs:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

你也可以交换下装饰器顺序(对顺序敏感的):

@ingredients
@bread
def strange_sandwich(food="--ham--"):
    print(food)

strange_sandwich()
#outputs:
##tomatoes#
#</''''''\>
# --ham--
#<\______/>
# ~salad~

更多阅读:参考这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值