利用亚运会,读懂 Python装饰器

阅读文本大概需要 5 分钟。

2018 年印度雅加达亚运会已接近尾声,中国在金牌榜和总奖牌榜都遥遥领先于第二名的日本,我也是一名体育爱好者,平时有比赛也会看。看到中国的国旗在海外飘扬,内心会格外的自豪,中国不再是任人宰割的「东亚病夫」。

今天我结合亚运会带大家理解下 Python 装饰器。

装饰器在我之前发的那篇「用 Python 玩微信,机器人陪你唠嗑」文章大家都见过,没看过的点击这里,我在创建话痨机器人函数和微智能机器人函数前都加了一句代码:

@robot.register()

可能当时大家不了解这句代码的含义,其实这就是装饰器。

装饰器是什么?

首先,什么是装饰器呢?Python 装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用 Python 装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。

比如我定义一个函数,孙杨在 200 米自由泳夺冠的函数。

def Freestyle200m():
    print('冠军:中国孙杨')

Freestyle200m()

# 结果:
冠军:中国孙杨

这时如果你想加一个功能进去,中国运动员夺冠就会升中国国旗。这时你会怎么做,简单粗暴的方法就是直接修改函数代码。

def Freestyle200m():
    print('冠军:中国孙杨')
    print('升中国国旗')

Freestyle200m()

# 结果:
冠军:中国孙杨
升中国国旗

无参数装饰器

如果下次又要加一个功能,你又要改一次代码,如果其他 100 多个夺冠项目都要加这个功能,你一个个复制粘贴,我这举的例子比较简单,如果是复杂的功能,会让人崩溃,代码位置一旦加错,更让人无语了。所以装饰器就可以发挥作用了。刚才说到,使用 Python 装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。这时我可以单独把升国旗这个功能定义成一个装饰器,装饰器的本质也是函数,它的返回值也是一个函数。

def Raising(func):
    def wrapper(*args, **kw):
        print('升国旗项目:%s' % func.__name__)
        return func(*args, **kw)
    return wrapper

这个装饰器就定义好了,它的作用是打印升国旗的项目名称。这样我们就可以用「@装饰器」的方式直接调用装饰器了。

@Raising
def Freestyle200m():
    print('冠军:中国孙杨')


Freestyle200m()

# 结果:
升国旗项目:Freestyle200m
冠军:中国孙杨

调用 Freestyle200m() 函数不仅会运行 Freestyle200m() 函数本身,还会在运行 Freestyle200m() 函数前执行装饰器的功能。

由于 Raising() 是一个装饰器,返回一个函数,所以,原来的 Freestyle200m() 函数仍然存在,只是现在同名的 Freestyle200m 变量指向了新的函数,于是调用 Freestyle200m() 将执行新函数,即在 Raising() 函数中返回的 wrapper() 函数。

装饰器中 wrapper() 函数的参数定义是(*args, **kw),说明接受任意参数的调用。在 wrapper() 函数内,首先打印升国旗项目,再紧接着调用原始函数 Freestyle200m()。

有参数装饰器

我们知道,颁奖的时候会升起获得金牌、银牌、铜牌的选手的国家国旗,演奏的国歌是获取金牌选手的国家的国歌,这时,我们想把奏哪个国家国歌参数化,装饰器本身就得传入一个参数。

def Raising(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('奏哪国国歌:%s \n升国旗项目:%s' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

上面定义的装饰器就定义了一个参数,这个参数是表示国家的,我们调用这个装饰器的时候必须传入这个参数去调用装饰器。

@Raising('中国')
def Freestyle200m():
    print('冠军:中国孙杨')


Freestyle200m()

# 结果
奏哪国国歌:中国 
升国旗项目:Freestyle200m
冠军:中国孙杨

调用之后的效果和下面的表达式是一样的。

Freestyle200m = Raising('中国')(Freestyle200m)

它首先执行 Raising('中国'),返回的是 decorator 函数,再调用返回的函数,参数是 Freestyle200m 函数,返回值最终是 wrapper 函数。

属性转换

我们知道,函数也是对象,它有 __name__ 等属性,但你去看经过 decorator 装饰之后的函数,它们的 __name__ 已经从原来 Freestyle200 变成了 wrapper。

print(Freestyle200m.__name__)

# 结果
wrapper

这是我们需要做个转换,wrapper.__name__ =func.__name__ ,每次都要这样写,很麻烦。你要知道,程序员很会偷懒的,所有就有了functools.wraps。这是 Python 内置的函数,它是专门做这种转换工作的。

import functools


def Raising(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('奏哪国国歌:%s \n升国旗项目:%s' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

这样,一个完整的带参数的装饰器就搞定了,当然,你也可以选择不带参数的装饰器。

import functools


def Raising(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('升国旗项目:%s' % func.__name__)
        return func(*args, **kw)
    return wrapper

调用多个装饰器

我们的选手夺冠了,我们很开心,一般会安排接受记者采访。我想加这个功能怎么办?没关系,我们可以同时调用多个装饰器。我们先定义一个记者采访的装饰器。

import functools


def Interview(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('准备接受采访的选手是:')
        return func(*args, **kw)
    return wrapper

可以调用这两个装饰器。

@Raising('中国')
@Interview
def Freestyle200m():
    print('冠军:中国孙杨')

# 结果
奏哪国国歌:中国 
升国旗项目:Freestyle200m
准备接受采访的选手是:
冠军:中国孙杨

被多个函数重复调用

装饰器同样可以被其他多个函数调用。
苏炳添,8 月 26 日,在雅加达亚运会田径男子 100 米的决赛中,苏炳添以 9 秒 92 打破亚运会纪录的成绩夺冠。

我们也可以定义苏炳添夺冠的函数来调用装饰器。

@Raising('中国')
@Interview
def HundredRace():
    print('冠军:中国苏炳添')

HundredRace()

# 结果
奏哪国国歌:中国 
升国旗项目:HundredRace
准备接受采访的选手是:
冠军:中国苏炳添

前些天,王者荣耀的游戏迷是比较高兴的,因为在「王者荣耀国际版」(AoV)首日表演项目决赛中,中国代表队经过激烈角逐,最终以 2:0 战胜中国台北代表队,获得本届亚运会电竞表演赛的首个冠军。以后,玩王者荣耀的伙伴们可以自豪的说:我是在为国家玩王者荣耀!

@Raising('中国')
@Interview
def KingGlory():
    print('王者荣耀冠军:中国队')


KingGlory()

# 结果
奏哪国国歌:中国 
升国旗项目:KingGlory
准备接受采访的选手是:
王者荣耀冠军:中国队

装饰器本质上只是个语法糖,使原本苦涩的代码结构变得甜蜜起来。

这样,之前 Python 机器人的项目中创造机器人函数前加的 @robot.register() 就好理解了,register 是用于注册消息配置的装饰器。

好了,今天的文章就讲到这里,希望能帮助大家对装饰器的理解。此文章如果对你有点帮忙的话希望大家能多给点支持,关注、点赞、转发对我都是一种鼓励,有什么问题欢迎在后台联系我,也可以在后台加入技术交流群,群里有大神,可以一起交流学习。

pk哥还没开通留言功能,觉得不错,点赞、转发朋友圈都是一种支持。

推荐阅读

Python让你的数据生成可视化图形

Python优雅写法,让你工作效率翻2倍

让代码和迈克杰克逊一起跳舞

以梦为马,不负韶华

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值