关闭

草根学Python(十六) 装饰器(逐步演化成装饰器)

336人阅读 评论(0) 收藏 举报
分类:

上一篇文章将通过解决一个需求问题来了解了闭包,本文也将一样,通过慢慢演变一个需求,一步一步来了解 Python 装饰器。

首先有这么一个输出员工打卡信息的函数:

def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')


punch()

输出的结果如下:

昵称:两点水  部门:做鸭事业部 上班打卡成功

然后,产品反馈,不行啊,怎么上班打卡没有具体的日期,加上打卡的具体日期吧,这应该很简单,分分钟解决啦。好吧,那就直接添加打印日期的代码吧,如下:

import time


def punch():
    print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')


punch()

输出结果如下:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功

这样改是可以,可是这样改是改变了函数的功能结构的,本身这个函数定义的时候就是打印某个员工的信息和提示打卡成功,现在增加打印日期的代码,可能会造成很多代码重复的问题。比如,还有一个地方只需要打印员工信息和打卡成功就行了,不需要日期,那么你又要重写一个函数吗?而且打印当前日期的这个功能方法是经常使用的,是可以作为公共函数给各个模块方法调用的。当然,这都是作为一个整体项目来考虑的。

既然是这样,我们可以使用函数式编程来修改这部分的代码。因为通过之前的学习,我们知道 Python 函数有两个特点,函数也是一个对象,而且函数里可以嵌套函数,那么修改一下代码变成下面这个样子:

import time


def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')


def add_time(func):
    print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
    func()


add_time(punch)

输出结果:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功

这样是不是发现,这样子就没有改动 punch 方法,而且任何需要用到打印当前日期的函数都可以把函数传进 add_time 就可以了,就比如这样:

import time


def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')


def add_time(func):
    print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
    func()


def holiday():
    print('天气太冷,今天放假')


add_time(punch)
add_time(holiday)

打印结果:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功
2018-01-09
天气太冷,今天放假

使用函数编程是不是很方便,但是,我们每次调用的时候,我们都不得不把原来的函数作为参数传递进去,还能不能有更好的实现方式呢?有的,就是本文要介绍的装饰器,因为装饰器的写法其实跟闭包是差不多的,不过没有了自由变量,那么这里直接给出上面那段代码的装饰器写法,来对比一下,装饰器的写法和函数式编程有啥不同。

import time


def decorator(func):
    def punch():
        print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
        func()

    return punch


def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')


f = decorator(punch)
f()

输出的结果:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功

通过代码,能知道装饰器函数一般做这三件事:

  1. 接收一个函数作为参数
  2. 嵌套一个包装函数, 包装函数会接收原函数的相同参数,并执行原函数,且还会执行附加功能
  3. 返回嵌套函数

可是,认真一看这代码,这装饰器的写法怎么比函数式编程还麻烦啊。而且看起来比较复杂,甚至有点多此一举的感觉。

那是因为我们还没有用到装饰器的 “语法糖” ,我们看上面的代码可以知道, Python 在引入装饰器 (Decorator) 的时候,没有引入任何新的语法特性,都是基于函数的语法特性。这也就说明了装饰器不是 Python 特有的,而是每个语言通用的一种编程思想。只不过 Python 设计出了 @ 语法糖,让 定义装饰器,把装饰器调用原函数再把结果赋值为原函数的对象名的过程变得更加简单,方便,易操作,所以 Python 装饰器的核心可以说就是它的语法糖。

那么怎么使用它的语法糖呢?很简单,根据上面的写法写完装饰器函数后,直接在原来的函数上加 @ 和装饰器的函数名。如下:

import time


def decorator(func):
    def punch():
        print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
        func()

    return punch

@decorator
def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')

punch()

输出结果:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功

那么这就很方便了,方便在我们的调用上,比如例子中的,使用了装饰器后,直接在原本的函数上加上装饰器的语法糖就可以了,本函数也无虚任何改变,调用的地方也不需修改。

不过这里一直有个问题,就是输出打卡信息的是固定的,那么我们需要通过参数来传递,装饰器该怎么写呢?装饰器中的函数可以使用 *args 可变参数,可是仅仅使用 *args 是不能完全包括所有参数的情况,比如关键字参数就不能了,为了能兼容关键字参数,我们还需要加上 **kwargs

因此,装饰器的最终形式可以写成这样:

import time


def decorator(func):
    def punch(*args, **kwargs):
        print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
        func(*args, **kwargs)

    return punch


@decorator
def punch(name, department):
    print('昵称:{0}  部门:{1} 上班打卡成功'.format(name, department))


@decorator
def print_args(reason, **kwargs):
    print(reason)
    print(kwargs)


punch('两点水', '做鸭事业部')
print_args('两点水', sex='男', age=99)

输出结果如下:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功
2018-01-09
两点水
{'sex': '男', 'age': 99}

至此,草根学 Python 入门系列文章结束了。如果感兴趣的话,可以关注微信公众号,回复 “Python” 获取更多的 Python 学习资料。

微信公众号

2
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

浅谈Python装饰器

浅谈Python装饰器 By 马冬亮(凝霜  Loki) 一个人的战争(http://blog.csdn.net/MDL13412) 前置知识 一级对象 Python将一切视为 objec t的...
  • MDL13412
  • MDL13412
  • 2014-03-30 22:07
  • 39415

Python实战小程序——装饰器

第四题:简述对Python装饰器的理解,写一个简单的装饰器。 要理解装饰器,我们先介绍一下几点python的基础知识。 1、作用域(命名空间)及变量生存周期 有过一点编程基础的都知道namesp...
  • misayaaaaa
  • misayaaaaa
  • 2016-11-04 16:15
  • 785

Python 装饰器的典型使用场景(1)

Python 装饰器的典型使用场景(1)
  • qq_26886929
  • qq_26886929
  • 2017-01-05 12:32
  • 1142

python(四)下:python装饰器详解

装饰器一、介绍 器:代表函数的意思。装饰器本质就是是函数 功能:装饰其他函数,就是为其他函数添加附加功能 被装饰函数感受不到装饰器的存在 原则: 不能修改被装饰的函数的源代码(比如线上环境) ...
  • fgf00
  • fgf00
  • 2016-08-25 23:52
  • 2070

深入理解Python 装饰器(decorator)

返璞归真, 看山还是山 刚看到Python装饰器时, 觉得很神奇。简单实验下,发现也就那么回事。但是慢慢的看到越来越多的装饰器。很多时候又不了解到底是怎么回事了。最后还是决定好好研究下。先看看一些实例...
  • TangHuanan
  • TangHuanan
  • 2015-04-17 11:12
  • 6964

深入浅出 Python 装饰器:16 步轻松搞定 Python 装饰器

目录[-] 1. 函数 2. 作用域 3. 变量解析规则 4. 变量生存周期 5. 函数参数 6. 嵌套函数 7. 函数是python世界里的一级类对象 8. 闭包 9. 装...
  • bluehawksky
  • bluehawksky
  • 2015-12-21 15:22
  • 4864

Python 装饰器

在stackoverflow上面看到一个关于Python中装饰器问题的回复,瞬间觉得作者简直是神人啊。 原文地址:http://stackoverflow.com/questions/739654...
  • Money_Bear
  • Money_Bear
  • 2013-09-16 14:28
  • 3221

Python-开发之路-装饰器&多层装饰器简介

装饰器、两层装饰器的简介,深入的还请继续深挖
  • qq_34409701
  • qq_34409701
  • 2016-06-05 15:37
  • 1097

Python多个装饰器的顺序

原文链接:http://www.cnblogs.com/nisen/p/6193426.html?utm_source=itdadao&utm_medium=referral 装饰器是Pyth...
  • jyhhhhhhh
  • jyhhhhhhh
  • 2017-01-20 02:51
  • 1852

python装饰器的执行过程

今天看到一句话:装饰器其实就是对闭包的使用,仔细想想,其实就是这回事,今天又看了下闭包,基本上算是弄明白了闭包的执行过程了。其实加上几句话以后就可以很容易的发现,思路给读者,最好自己总结一下,有助于理...
  • You_are_my_dream
  • You_are_my_dream
  • 2016-11-15 00:52
  • 1084
    个人资料
    • 访问:283045次
    • 积分:4095
    • 等级:
    • 排名:第8745名
    • 原创:133篇
    • 转载:7篇
    • 译文:0篇
    • 评论:102条
    个人资料
    博客专栏