初窥Python(五)——python中的decorator


1. 介绍

decorator是用来在代码运行期间动态增加功能的,本质上是一个返回函数的高阶函数。假设现在有这样一种需求,即在每个函数调用前记录日志,记录被调用的函数名称,可以这样实现:

def log(func):
    def wrapper(*args, **kwargs):
        print "CALL %s()" % func.__name__
        return func(*args, **kwargs)
    return wrapper

def sayHi():
    print "Hi, Buddy."

def sayHello():
    print "Hello, Buddy."

# 调用函数,记录日志
log(sayHi)()
# 输出为
# CALL sayHi()
# Hi, Buddy.
log(sayHello)()
# 输出为
# CALL sayHello()
# Hello, Buddy.

这种方法确实实现了记录日志的功能,但每次这么调用未免太过繁琐,decorator因此出现。

2. 使用

其实,之前定义的log函数即为一个decorator,只是使用方式不正确:

@log
def sayHi():
    print "Hi, Buddy."

sayHi()
# 输出为
# CALL sayHi()
# Hi, Buddy.

@log
def sayHello():
    print "Hello, Buddy."

sayHello()
# 输出为
# CALL sayHello()
# Hello, Buddy.

可以看到,使用decorator非常简单方便,def sayHi():前的@log相当于将sayHi作为参数传入log函数中,并将返回值赋给sayHi,即:

sayHi = log(sayHi)

但是细心的读者不难发现,这样一来函数sayHi__name__属性发生变化,由之前的sayHi变为wrapper,使用python内置的functools.wraps方法可以解决这一问题,改造后的decorator如下:

import functools 

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print "CALL %s()" % func.__name__
        return func(*args, **kwargs)
    return wrapper

让我们更进一步,使用三层嵌套的decorator,允许再多传入一次参数:

import functools

def log(text="CALL")
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print "%s %s()" % (text, func.__name__)
            return func(*args, **kwargs)
        return wrapper
    return decorator

这次我们可以选择传入一个表示函数运行状态的字符串,由于多了一层嵌套,使用时也会有些变化:

@log("EXECUTE")
def sayHi():
    print "Hi, Buddy."

sayHi()
# 输出为
# EXECUTE sayHi()
# Hi, Buddy.

修改过后嵌套使用为:

sayHi = log("EXECUTE")(sayHi)

3. 拓展

3.1

修改log函数,使该decorator既可以通过

@log

使用,又可以通过

@log("EXECUTE")

使用:

import functools

def log(text="CALL"):
    if callable(text):
        func = text
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print "CALL %s()" % func.__name__
            return func(*args, **kwargs)
    else:
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                print "%s %s()" % (text, func.__name__)
                return func(*args, **kwargs)
            return wrapper
        return decorator

两种使用方式:

@log
def sayHi():
    print "Hi, Buddy."

sayHi()
# 输出为
# CALL sayHi()
# Hi, Buddy.

@log("EXECUTE")
def sayHello():
    print "Hello, Buddy."

sayHello()
# 输出为
# EXECUTE sayHello()
# Hello, Buddy.
3.2

修改log函数,使该decorator在函数调用前及函数调用后分别输出一条日志:

import functools

def log(func):
    def wrapper(*args, **kwargs):
        print "CALL BEGINNING"
        call = func(*args, **kwargs)
        print "CALL ENDING"
        return call
    return wrapper

使用该decorator

@log
def sayHi():
    print "Hi, Buddy."

sayHi()
# 输出为
# CALL BEGINING
# Hi, Buddy.
# CALL ENDING

参考资料:

廖雪峰的官方网站

时间: 2024-10-07 07:44:08

初窥Python(五)——python中的decorator的相关文章

初窥Python(三)——python版本升级及ipython的安装使用

在使用 CentOS6.6 X64 系统时,由于系统自带的 python 版本为 2.6.6,而 2.x 版本中当前普遍使用的为2.7 版本,所以我们要对系统的 python 版本做一个升级.ipython 是一个增强版的shell,支持TAB补全,自动缩进等供能,比默认的 python shell 要好用很多.下面主要介绍如何升级 python 版本,安装 pip 并使用 pip install 安装 ipython,搭建一个简易的 python 环境. 1.官网下载安装包: [[email 

【Python五篇慢慢弹(5)】‘类’过依然继续前行,直至ending再出发

‘类’过依然继续前行,直至ending再出发 作者:白宁超 2016年10月10日22:36:57 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给出的pythondoc入门资料包含了基本要点.本文是对文档常用核心要点进行梳理,简单冗余知识不再介绍,作者假使你用c/java/c#/c++任一种语言基础.本系列文章属于入门内容,老鸟可以略看也可以略过,新鸟可以从篇一<快速上手学python>先接触下python怎样安装与运行,以及pychar

【Python五篇慢慢弹(4)】模块异常谈python

模块异常谈python 作者:白宁超 2016年10月10日12:08:31 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给出的pythondoc入门资料包含了基本要点.本文是对文档常用核心要点进行梳理,简单冗余知识不再介绍,作者假使你用c/java/c#/c++任一种语言基础.本系列文章属于入门内容,老鸟可以略看也可以略过,新鸟可以从篇一<快速上手学python>先接触下python怎样安装与运行,以及pycharm编辑器的使用和配置:

Python之数字中的函数

Python之数字中的函数 本篇在与介绍Python语法中关于数字部分的各种函数 1.标准类型函数 数字中的标准类型函数适用于所有的标准类型,常用的有三种:cmp(), str(), type(). 下面将分别介绍 cmp()函数: cmp(x, y) 接收两个参数,对这两个参数进行比较,并返回值.若x>y,返回1:若x=y,返回0:若x<y,返回-1. >>>cmp(2, 4)-1 str()函数: str(object) 将给定对象object返回为格式化好的字符串,常用

Python——五分钟理解元类(metaclasses)

“元类的魔幻变化比 99% 的用户所担心的更多,当你搞不懂是否真的需要用它的时候,就是不需要.” —Tim Peters 本文源于在 PyCon UK 2008 上的一个快速演讲. 元类被称为 Python 中的“深奥的巫术”.尽管你需要用到它的地方极少(除非你基于zope 编程),可事实上它的基础理论其实令人惊讶地易懂. 一切皆对象 一切皆对象 一切都有类型 “class”和“type”之间本质上并无不同 类也是对象 它们的类型是 type 以前,术语 type 用于内置类型,而术语 clas

Python统计列表中的重复项出现的次数的方法

前言 在实际工作和学习中,经常会遇到很多重复的数据,但是我们又必须进行统计,所及这里简单介绍一下统计列表中重复项的出现次数的简单方法. 实例 本文实例展示了Python统计列表中的重复项出现的次数的方法,是一个很实用的功能,适合Python初学者学习借鉴.具体方法如下: #方法1: mylist = [1,2,2,2,2,3,3,3,4,4,4,4] myset = set(mylist)  #myset是另外一个列表,里面的内容是mylist里面的无重复 项 for item in myset

Python 五、Python函数

一.函数概述 1.函数的基础概念 函数是python为了代码最大程度地重用和最小化代码冗余而提供的基础程序结构. 函数是一种设计工具,它能让程序员将复杂的系统分解为可管理的部件 函数用于将相关功能打包并参数化 在python中可以创建4种函数: 全局函数:定义在模块中 局部函数:嵌套于其它函数中 lambda(匿名)函数:仅是一个表达式 方法:与特定数据类型关联的函数,并且只能与数据类型关联一起使用 函数和过程的联系:每个Python函数都有一个返回值,默认为None,也可以使用"return

[Docker]在Python和IPython中使用Docker

现在Docker是地球上最炙手可热的项目之一,就意味着人民实际上不仅仅是因为这个才喜欢它. 话虽如此,我非常喜欢使用容器,服务发现以及所有被创造出的新趣的点子和领域来切换工作作为范例. 这个文章中我会简要介绍使用python中的docker-py模块来操作Docker 容器,这里会使用我喜爱的编程工具IPython. 安装docker-py 首先需要docker-py.注意这里的案例中我将会使用Ubuntu Trusty 14.04版本. $ pip install docker-py IPyh

Python 可视化Twitter中指定话题中Tweet的词汇频率

CODE: #!/usr/bin/python # -*- coding: utf-8 -*- ''' Created on 2014-7-8 @author: guaguastd @name: plot_frequencies_words.py ''' if __name__ == '__main__': #import json # import Counter from collections import Counter # import search from search impor

Python 查找Twitter中最流行(转载最多)的10个Tweet

CODE: #!/usr/bin/python # -*- coding: utf-8 -*- ''' Created on 2014-7-4 @author: guaguastd @name: find_popular_retweets.py ''' # Finding the most popular retweets def popular_retweets(statuses): retweets = [ # Store out a tuple of these three values.