专栏首页BigYoung小站我终于弄懂了Python的装饰器(一)

我终于弄懂了Python的装饰器(一)

一、装饰器基础(什么是装饰器)

Python的函数是对象

要了解装饰器,您必须首先了解函数是Python中的对象。这具有重要的联系。

让我们来看一个简单的例子:

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

print(shout())
# 输出 : 'Yes!'

# 作为一个对象,您可以像其他对象一样将函数赋给变量
scream = shout

#注意我们不使用括号:我们没有调用函数
#我们将函数“shout”放入变量“scream”。
#这意味着您可以从“scream”中调用“shout”:

print(scream())
# 输出: 'Yes!'

#除此之外,这意味着您可以删除旧名称'shout',该功能仍可从'scream'访问

del shout

try:
    print(shout())
except NameError as e:
    print(e)
    #输出: "name 'shout' is not defined"

print(scream())
# 输出: 'Yes!'

请记住这一点,我们将在不久后回头再说。

Python函数的另一个有趣特性是可以在另一个函数中定义它们!

def talk():

    # 您可以在“talk”中动态定义一个函数...
    def whisper(word="yes"):
        return word.lower()+"..."

    # ...并且可以立马使用它。
    print(whisper())

#您每次调用“talk”,都会定义“whisper”,然后在“talk”中调用“whisper”。

talk()
# 输出: 
# "yes..."

# 但是"whisper"不存在"talk"定义以外的地方:

try:
    print(whisper())
except NameError as e:
    print(e)
    #输出 : "name 'whisper' is not defined"

函数参考

OK,应该还在看吧?现在开始有趣的部分...

您已经看到函数是对象。因此,函数:

  • 可以分配给变量
  • 可以在另一个函数中定义

这意味着一个函数可以return另一个功能

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"
talk = getTalk()

#您可以看到“talk”是一个函数对象:
print(talk)
#输出 : <function shout at 0xb7ea817c>

#函数对象返回的内容:
print(talk())
#输出 : 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)
#输出: 
#I do something before then I call the function you gave me
#Yes!

好吧,您只具备了解装饰器所需的所有信息。您会看到,装饰器是“包装器(wrappers)”,这意味着**它们使您可以在装饰函数之前和之后执行代码,**而无需修改函数本身的代码内容。

手工进行装饰

您将知道如何进行手动操作:

#装饰器是讲另外一个函数作为参数的函数
def my_shiny_new_decorator(a_function_to_decorate):

    # 在内部,装饰器动态定义一个函数:包装器(wrappers)。
    # 此功能将被包装在原始功能的外部,以便它可以在代码之前和之后执行代码。
    def the_wrapper_around_the_original_function():

        # 在调用原始函数之前,将要执行的代码放在此处
        print("Before the function runs")

        #在此处调用函数(使用括号)
        a_function_to_decorate()

        # 在调用原始函数后,将要执行的代码放在此处
        print("After the function runs")

    #至此,“a_function_to_decorate”从未执行过。
    #我们返回刚刚创建的包装函数。
    #包装器包含函数和在代码之前和之后执行的代码。随时可以使用!
    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() 
#输出: 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()

#输出:
#Before the function runs
#I am a stand alone function, don't you dare modify me
#After the function runs

现在,您可能希望每次调用a_stand_alone_functiona_stand_alone_function_decorated都调用它。这很简单,只需a_stand_alone_function用以下方法返回的函数覆盖my_shiny_new_decorator

a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#输出:
#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()  
#输出:  
#Before the function runs
#Leave me alone
#After the function runs

是的,仅此而已。@decorator只是实现以下目的的捷径:

another_stand_alone_function = 
my_shiny_new_decorator(another_stand_alone_function)

装饰器只是装饰器设计模式的pythonic变体。Python中嵌入了几种经典的设计模式来简化开发(例如迭代器)。

当然,您可以累加装饰器:

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()
#输出: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#输出:
#</''''''\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

使用Python装饰器语法:

@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~

本文分享自微信公众号 - BigYoung小站(bigyoungs),作者:Young文人

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-07

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 我终于弄懂了Python的装饰器(四)

    如要制作通用装饰器(无论参数如何,您都可以将其应用于任何函数或方法),则只需使用*args, **kwargs:

    BigYoung小站
  • Ldap3 库使用方法(三)

    以上是Ldap3库对AD的User和OU新增的方法。具体的文档,可以参考官网文档:Ldap3 文档

    BigYoung小站
  • Ldap3 库使用方法-完整版修改连接

    BigYoung小站
  • R语言新神器visdat包(一行代码看穿整个数据集)

    通过经典的airquality数据集(其中包含有关1973年5月至9月纽约每日空气质量测量的信息)展示vis_dat()的功能。

    用户1359560
  • Python每日一题:装饰器(一)

    关于Python装饰器,考点很多,这里在网上找到一个对Python装饰器解释的很详细的回答。因为是英语的,并且比较长,将通过两篇来进行翻译。原文链接如下:

    用户7685359
  • 初探numpy——numpy常用通用函数

    快速的逐元素数组函数,也可以称为ufunc,对ndarray数据中的元素进行逐元素操作的函数

    LRainner
  • 60题PyTorch简易入门指南,做技术的弄潮儿!

    48.构造一个transform,将三通道(0,1)区间的数据转换成(-1,1)的数据

    Datawhale
  • JavaScript设计模式(3)——Revealing Module(揭示模块)模式

    这种模式能够在私有范围内定义所有的函数和变量,并返回一个匿名对象,它拥有指向私有函数的指针,该私有函数是希望展示为公有的方法。

    悠扬前奏
  • JavaScript设计模式(2)——Module(模块)模式

    对象字面量表示法中,一个对象被描述为一组包含在大括号({})中,以逗号分割的name/value对。 对象内的名称可以是字符串或者标识符,用冒号结尾。 最后...

    悠扬前奏
  • python文件

    东风冷雪

扫码关注云+社区

领取腾讯云代金券