Python 之 decorator装饰器

最近看到 廖雪峰 的Python教程,对于学习Python的人来说,这可谓是一大福音,没有冗长的废话,只有最通俗简短的语言,以及最清晰的例子讲解。

下面是我对 装饰器 这一小节的总结, 以及自己的理解。

注:【本文中的代码参考上述教程】

很多时候我会把Python的很多语法与C++相融合,在C++中,函数的名称即为函数的地址,我们可以通过定义成为"函数指针"的变量,并且将函数名称赋值给该变量,那么我们在调用函数的时候,就可以直接使用该变量调用函数。

例如下面的C++的代码就是一个简单的函数指针的定义以及调用的过程。

#include <iostream>
using namespace std;

int f(int x, int y)
{
    return x+y;
}

int main(void)
{
    int (*fptr)(int,int) ; //定义函数指针便令fptr。
    fptr = f;
    cout << (*fptr)(2,3) << endl;
    return 0;
}

在Python中,函数也是对象,并且函数对象可以被赋值给变量。通过该变量也可以调用函数,像上面一样。但是Python是动态语言,不用那么复杂的定义变量的类型,就像上面的fptr。直接可以赋值即可。

例如下面的简单的Python代码:

def date():
    print "2014-11-5"
f = date // f便是函数指针
date()
f()

函数对象都有一个属性__name__,可以得到函数的名字: 例如: f.__name__ 为date。

下面开始进入正题,decorator是什么,decorator是一个返回函数的高阶函数。例如,我们的一个简单的log函数,该函数在每次函数调用时做日志的工作。但是我们又不希望去修改每一个需要计入日志的函数的实现代码,那么decorator就派上用场了。

def log(func):
    def wrapper(*args, **kw):
        print "[log] : call %s(): " %func.__name__
        return func(*args, **kw)
    return wrapper

首先,上面的函数log的参数是一个函数对象,返回值也是一个函数。

那么怎么将log函数用起来呢?很简单,在 要传入log作为参数 的函数定义的前面前面使用 @log 关键句即可。即像下面这样:

@log
def date():
    print "2014-11-5"

这样每次调用date(),就相当于在调用log(date).  不添加 @log语句在date前,输出的结果是:

2014-11-5

加上@log语句之后的输出结果为:

[log] : call date()

2014-11-5

也就是说在添加了log之后,函数的调用等价与log(date),log函数的参数是date,返回的是wrapper。可以看到,wrapper可以接受任何参数的函数,在wrapper函数内部,首先打印log行,再调用原始的函数。

这里的简答的理解就是,函数在调用之前,如果被做了包裹,也就是在函数定义之前使用了@关键字,那么我们调用的就是相应的包裹函数。例如上面的调用date时调用的其实是log(date)。

这里可能存在的问题时,前面讲到说函数是个对象,有一个__name__属性,但是你会发现上述的date函数在经过装饰之后的__name__属性是wrapper。这样对于这个__name__有依赖的代码就会出现问题,因此我们需要的是 wrapper.__name__ = date.__name__的语句的功能,这个在python中可以简单使用下面的代码来实现。也就是装饰函数的完整版本:

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

使用模块functools中得wraps函数就可以实现。

上述讲述了装饰者模式,下面给出一个比较完整的例子。

#!/usr/bin/env python
import sys

#decorator

import functools
def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print "[log] : call %s(): " %func.__name__
        f = func(*args, **kw)
        return f
    return wrapper

date()
print date.__name__

代码结果输出为:

[log] : call date():

2014-11-5

date

如果log函数本身是有参数的话,那么decorator模式就需要再加上一层传入log函数本身的参数,代码为:

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

print "Test full implementation of decorator"
@log("paramete of log")
def date():
    print "2014-11-5"
date()

此时调用log函数就等价于调用 log("parameter of log")(date) ,函数log的返回值是decorator函数,再将date作为参数传递给decorator函数,实现调用。

下面用一个最完整的例子作为结束:

#!/usr/bin/env python

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

def log2(func):
    def decorator(*args, **kw):
        return func(*args, **kw)
    return decorator

@log("decorator need parameter version1")
@log("decorator need parameter version2")
def date2(x,y):
    print "2014-11-5"
    print "x, y ", x, y
    return x

date2 = log('execute1')(date2)
date2 = log('execute2')(date2)
date2 = log('execute3')(date2)
date2(2, 3)

输出结果为:

execute3 date2()

execute2 date2()

execute1 date2()

decorator need parameter version1 date2()

decorator need parameter version2 date2()

2014-11-5

x, y  2 3

参考资料:Python之decorator

时间: 2024-10-10 22:55:02

Python 之 decorator装饰器的相关文章

Python 之 decorator 装饰器

python 在语法上对装饰模式进行了支持. 一.问题 有如下的一个函数, def sum(x, y):     print "x+y:", x+y 我们需要在每次调用 sum 函数的时候打印日志,但是我们并不希望修改sum函数. 二.解决方案 Solution 1: 最 brute-force 的方法,定义一个包裹函数, 在这个包裹函数的内部调用sum 函数(包裹函数和 sum 函数最好具有相同的参数,和返回类型) def wrapper1(x, y):     print &quo

python学习笔记--装饰器

1.首先是一个很无聊的函数,实现了两个数的加法运算: def f(x,y): print x+y f(2,3) 输出结果也ok 5 2.可是这时候我们感觉输出结果太单一了点,想让代码的输出多一点看起来不这么单调: def showInfo(fun): def wrap(x,y): print "The function before" func(x,y) print "The function after" return wrap def f(x,y): print

Python深入05 装饰器

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法或者类进行加工.在Python中,我们有多种方法对函数和类进行加工,比如在Python闭包中,我们见到函数对象作为某一个函数的返回结果.相对于其它方式,装饰器语法简单,代码可读性高.因此,装饰器在Python项目中有广泛的应用. 装饰器最早在Python 2.5中出现,它最初被用于加工函数和方法这样

[python基础]关于装饰器

在面试的时候,被问到装饰器,在用的最多的时候就@classmethod ,@staticmethod,开口胡乱回答想这和C#的static public 关键字是不是一样的,等面试回来一看,哇,原来是这样,真佩服我当时厚着脸皮回答的那些问题... OK,先来张图看看装饰器内容: OK,我们留下一个印象,然后我们看实际的场景来操作. 我们先看一个方法: __author__ = 'bruce' def do_sth(): print 'some thing has been done' if __

浅显易懂的谈一谈python中的装饰器!!

hello大家好~~我是稀里糊涂林老冷,一天天稀里糊涂的. 前一段时间学习了装饰器,觉着这东西好高大上哇靠!!哈哈,一定要总结一下,方便以后自己查阅,也希望帮助其他伙伴们共同进步! 装饰器: 大家可以这样理解,装饰器是运用闭包的基本原理,对一个目标函数进行装饰.即是在执行一个目标函数之前.之后执行一些特定的事情. 学习装饰器一定要有闭包的基础知识,如果对闭包不是特别理解的话,可以参考我之前的博文http://www.cnblogs.com/Lin-Yi/p/7305364.html,也可以学习其

十、PYTHON 学习之装饰器加深理解

在第六章已经有所介绍,这里看到一篇比较好的文章转过来. 基本概念 装饰器是23z种设计模式之一,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理, Web权限校验, Cache等. 很有名的例子来理解,咖啡,加糖的咖啡,加牛奶的咖啡. 本质上,还是咖啡,只是在原有的东西上,做了"装饰",使之附加一些功能或特性. 例如记录日志,需要对某些函数进行记录 笨的办法,每个函数加入代码,如果代码变了,就悲催了 装饰器的办法,定义一个专门日志记录的装饰器,对需要的函数进行装饰.

Python进阶之装饰器

函数也是对象 要理解Python装饰器,首先要明白在Python中,函数也是一种对象,因此可以把定义函数时的函数名看作是函数对象的一个引用.既然是引用,因此可以将函数赋值给一个变量,也可以把函数作为一个参数传递或返回.同时,函数体中也可以再定义函数. 装饰器本质 可以通过编写一个纯函数的例子来还原装饰器所要做的事. def decorator(func):          def wrap():         print("Doing someting before executing fu

1.16 Python基础知识 - 装饰器

Python中的装饰器就是函数,作用就是包装其他函数,为他们起到修饰作用.在不修改源代码的情况下,为这些函数额外添加一些功能,像日志记录,性能测试等.一个函数可以使用多个装饰器,产生的结果与装饰器的位置顺序有关. 装饰器基本形式: @装饰器1 def 函数1: 函数体 相当于:==> 函数1 = 装饰器1(函数1) 装饰器特点: 1.不修改源代码的调用方式 2.不修改源代码内容 3.装饰器有高阶函数与递归函数相融合的特点 多个装饰器修饰,示例: @foo @spam def bar():pass

ZMAN的学习笔记之Python篇:装饰器

年前工作事务比较繁琐,我只能用零碎的时间继续学习Python,决定开一个系列的博文,作为自己深入学习Python的记录吧.名字也取好了,就叫<ZMAN的学习笔记之Python篇>~开篇是关于装饰器的,春节假期码的字哈哈~就让我们开始吧! 本文的例子都是自己想的,如果不是很合适,请大家提出宝贵意见哈~谢谢啦! 一.为什么要用“装饰器” 比如我们写了如下一段代码: # 打印0~99 def func(): for i in range(100): print(i) 我们想要监测执行这个函数花费了多