引言:

  装饰器是python面向对象编程三大器之一,另外两个迭代器、生成器只是我现在还没有遇到必须使用的场景,等确实需要用到的时候,在补充资料;装饰器在某些场景真的是必要的,比如定义了一个类或者一个函数,在后面的编程过程中发现需要对定义好的类增加一些功能;或者说有一些功能是某一类函数公用的功能,可以将这个功能做成装饰器来装饰这一类功能;场景还有很多,先来大体了解下装饰器的创建以及基本的实现原理;

文章来源:

http://blog.csdn.net/TangHuanan/article/details/45094497

正文:

刚看到Python装饰器时, 觉得很神奇。简单实验下,发现也就那么回事。但是慢慢的看到越来越多的装饰器。很多时候又不了解到底是怎么回事了。

最后还是决定好好研究下。

先看看一些实例, 然后再来分析下原理 
假设我们有如下的基本函数

def do_something():
    ):
        pass
    print "play game"

do_something()

结果如下:

play game

需求1: 统计函数的执行时间

1. 不是装饰器的装饰器

import time
def decorator(fun):
    start = time.time()
    fun()
    runtime = time.time()-start
    print runtime

def do_something():
    ):
        pass
    print "play game"

decorator(do_something)

结果如下:

play game
0.0299999713898

这种实现看上去还可以,但是每次调用的是decorator,还要把函数作为一个参数传入。这样需要修改调用的地方,使用起来就不方便了。

2. 最简单的装饰器

import time
def decorator(fun):
    def wrapper():
        start = time.time()
        fun()
        runtime = time.time()-start
        print runtime
    return wrapper
@decorator
def do_something():
    ):
        pass
    print "play game"

do_something()

结果如下:

play game
0.0329999923706

装饰器是在函数定义时前面加@,然后跟装饰器的实现函数。可以看出,现在只要直接调用do_something就可以了。调用的地方不要作任何修改。

3. 目标函数带固定参数的装饰器

import time
def decorator(fun):
    def wrapper(name):
        start = time.time()
        fun(name)
        runtime = time.time()-start
        print runtime
    return wrapper
@decorator
def do_something(name):
    ):
        pass
    print "play game " + name

do_something("san guo sha")

结果如下:

play game san guo sha
0.039999961853

实现很简单, 就是给wrapper函数参加相同的参数

4. 目标函数带不固定参数的装饰器

import time
def decorator(fun):
    def wrapper(*args, **kwargs):
        start = time.time()
        fun(*args, **kwargs)
        runtime = time.time()-start
        print runtime
    return wrapper
@decorator
def do_something(name):
    ):
        pass
    print "play game " + name

@decorator
def do_something2(user, name):
    ):
        pass
    print user+" play game " + name

do_something("san guo sha")
do_something2("wang xiao er","san guo sha")

结果如下:

play game san guo sha
0.029000043869
wang xiao er play game san guo sha
0.0310001373291

需求2: 目标函数每次调用重复执行指定的次数

5. 让装饰器带参数

import time
def decorator(max):
    def _decorator(fun):
        def wrapper(*args, **kwargs):
            start = time.time()
            for i in xrange(max):
                fun(*args, **kwargs)
            runtime = time.time()-start
            print runtime
        return wrapper
    return _decorator
@decorator()
def do_something(name):
    ):
        pass
    print "play game " + name

do_something("san guo sha")

结果如下:

play game san guo sha
play game san guo sha
0.0600001811981

6. 原理

看了这么多实例, 装饰器的基本类型也基本上都有了。是不是清楚了呢? 
如果还是不清楚,那就继续看下面的内容。

1 不带参数的装饰器

@a_decorator
def f(...):
    ...

#经过a_decorator后, 函数f就相当于以f为参数调用a_decorator返回结果。
f = a_decorator(f)

来分析这个式子, 可以看出至少要满足以下几个条件 
1. 装饰器函数运行在函数定义的时候 
2. 装饰器需要返回一个可执行的对象 
3. 装饰器返回的可执行对象要兼容函数f的参数

2 验证分析

1 装饰器运行时间

import time
def decorator(fun):
    print "decorator"
    def wrapper():
        print "wrapper"
        start = time.time()
        fun()
        runtime = time.time()-start
        print runtime
    return wrapper
@decorator
def do_something():
    ):
        pass
    print "play game"

结果如下:

decorator

可以看出, 这里的do_something并没有调用, 但是却打印了decorator, 可wrapper没有打印出来。也就是说decorator是在do_something调用的时候执行的。

2 返回可执行的对象

import time
def decorator(fun):
    print "decorator"
    def wrapper():
        print "wrapper"
        start = time.time()
        fun()
        runtime = time.time()-start
        print runtime
    return None
@decorator
def do_something():
    ):
        pass
    print "play game"

do_something()

结果如下:

decoratorTraceback (most recent call last):
  File , in <module>
    do_something()
TypeError: 'NoneType' object is not callable

3 兼容函数f的参数

import time
def decorator(fun):
    print "decorator"
    def wrapper():
        print "wrapper"
        start = time.time()
        fun()
        runtime = time.time()-start
        print runtime
    return wrapper
@decorator
def do_something(name):
    ):
        pass
    print "play game"

do_something("san guo sha")

结果如下:

decoratorTraceback (most recent call last):
  File , in <module>
    do_something("san guo sha")
TypeError: wrapper() takes no arguments ( given)

看到这里, 至少对不带参数的装饰器应该全弄清楚了, 也就是说能到看山还是山了。

3 带参数的装饰器

这里就给一个式子, 剩下的问题可以自己去想

@decomaker(argA, argB, ...)
def func(arg1, arg2, ...):
    pass
#这个式子相当于
func = decomaker(argA, argB, ...)(func)

4 被装饰过的函数的函数名

import time
def decorator(fun):
    def wrapper():
        start = time.time()
        fun()
        runtime = time.time()-start
        print runtime
    return wrapper
@decorator
def do_something():
    print "play game"

print do_something.__name__

结果如下:

wrapper

可以看出, do_something的函数名变成了wrapper,这不是我们想要的。原因估计各位也都清楚了。那要怎么去解决呢?

import time
def decorator(fun):
    def wrapper():
        start = time.time()
        fun()
        runtime = time.time()-start
        print runtime
    wrapper.__name__ = fun.__name__
    return wrapper
@decorator
def do_something():
    print "play game"

print do_something.__name__

结果如下:

do_something

但是这个看起来是不是很不专业, python的unctools.wraps提供了解决方法

import time
import functools
def decorator(fun):
    @functools.wraps(fun)
    def wrapper():
        start = time.time()
        fun()
        runtime = time.time()-start
        print runtime
    return wrapper
@decorator
def do_something():
    print "play game"

print do_something.__name__

结果如下:

do_something

到此为止, 你是不是觉得已经完全明白了呢? 
但事实是, 这其实还不够

7. 装饰器类

需求3: 让函数只能运行指定的次数 
前面我们讲的都是函数式的装饰器, 那么类能不能成为装饰器呢?

import time
import functools 

class decorator(object):
    def __init__(self, max):
        self.max = max
        self.count =
    def __call__(self, fun):
        self.fun = fun
        return self.call_fun

    def call_fun(self, *args, **kwargs):
        self.count +=
        if ( self.count == self.max):
            print "%s run more than %d times"%(self.fun.__name__, self.max)
        elif (self.count<self.max):
            self.fun(*args, **kwargs)
        else:
            pass

@decorator()
def do_something():
    print "play game"
@decorator()
def do_something1():
    print "play game 1"
):
    do_something()
    do_something1()

结果如下:

play game
play game
play game
play game
play game
play game
play game
play game
play game
play game
play game
play game
play game
play game
play game
play game
play game
play game
do_something run more than  times
play game
play game
play game
play game
play game
do_something1 run more than  times

是不是感觉有点怪, 但它确实是可行的。 
在Python中, 其实函数也是对象。 反过来, 对象其实也可以像函数一样调用, 只要在类的方法中实现__call__方法。回想一下创建对象的过程

class A:
    def __init__(self):
        pass
a = A()

这其实和函数调用没什么区别, 那么把这个式子代入到之前两个装饰器的式子中,结果如下: 
带参数的装饰器 
fun = A.__init__(args)(fun) 
不带参数的装饰器 
fun = A.__init__(fun)()

现在装饰器的内容基本差不多了。 还有一些问题, 可以自己去尝试研究。

还有几个问题如下: 
1. 类装饰器(装饰器装饰的对象是类) 
2. 类函数装饰器(装饰器装饰的对象是类的函数) 
3. 多个装饰器一起使用(函数嵌套)

python装饰器(docorator)详解的更多相关文章

  1. python装饰器大详解

    1.作用域 在python中,作用域分为两种:全局作用域和局部作用域. 全局作用域是定义在文件级别的变量,函数名.而局部作用域,则是定义函数内部. 关于作用域,我要理解两点:a.在全局不能访问到局部定 ...

  2. python装饰器学习详解-函数部分

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 最近阅读<流畅的python>看见其用函数写装饰器部分写的很好,想写一些自己的读书笔记. ...

  3. python装饰器使用详解

    装饰器 '''装饰器:就是闭包(闭包的一个应用场景) -- 把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数 优点: -- 丰富了原有函数的功能 -- 提高了程序的可拓展性''' 开 ...

  4. 进阶Python:装饰器 全面详解

    进阶Python:装饰器 前言 前段时间我发了一篇讲解Python调试工具PySnooper的文章,在那篇文章开始一部分我简单的介绍了一下装饰器,文章发出之后有几位同学说"终于了解装饰器的用 ...

  5. python 函数及变量作用域及装饰器decorator @详解

    一.函数及变量的作用   在python程序中,函数都会创建一个新的作用域,又称为命名空间,当函数遇到变量时,Python就会到该函数的命名空间来寻找变量,因为Python一切都是对象,而在命名空间中 ...

  6. (十)装饰器模式详解(与IO不解的情缘)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...

  7. 涉及模式之 装饰器模式详解(与IO不解的情缘)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...

  8. Java 装饰器模式详解

    转载请注明出处:http://blog.csdn.net/zhaoyanjun6/article/details/56488020 前言 在上面的几篇文章中,着重介绍了Java 中常见的 IO 相关知 ...

  9. Python装饰器详解

    python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...

随机推荐

  1. 使用webpack搭建vue项目中遇到的问题

    1:data数据文件经试验,需要放在生成的build文件夹中才能生效,但是应该把data文件夹先放在src中,然后如何定义config文件,让其复制过去? new CopyWebpackPlugin( ...

  2. pipelinedb Continuous transforms 操作

    Continuous transforms 可以进行数据的转换,数据是不进行存储,主要是可以加入到其他的stream pipeline 中,或者写到其他外部 存储中,和存储过程结合使用,当前默认内置一 ...

  3. oracle C# 访问

    使用oracle的odp.net 进行oracle数据库的访问对于进行oracle数据库的开发来说是比较方便的,使用的方式与ADO.net 是一致的. 一下为使用的测试 1. 安装必要的oracle ...

  4. linux平台下防火墙iptables原理

    iptables简单介绍 netfilter/iptables(简称为iptables)组成Linux平台下的包过滤防火墙,与大多数的Linux软件一样,这个包过滤防火墙是免费的,它能够取代昂贵的商业 ...

  5. 使用ioctl获取网卡统计信息

    ethtool -S获取接口统计信息总共分三步: 1.获取统计项个数,使用SIOCETHTOOL+ETHTOOL_GSSET_INFO 2.(可选)获取统计项名字,使用SIOCETHTOOL+ETHT ...

  6. hybrid app、react-native 区别

    hybrid app.react-native 区别: 项目 hybrid app react-native 组件 用HTML.CSS.JavaScript实现页面的制作,然后运行在Webview上( ...

  7. postman 中 form-data、x-www-form-urlencoded、raw、binary的区别

    区别 form-data: 就是http请求中的multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开.既可以上传键值对,也可以上传文件.当上传的字段是文件 ...

  8. hihocoder 1513 小Hi的烦恼——bitset

    题目:http://hihocoder.com/problemset/problem/1513 自带的题解写得很好…… #include<cstdio> #include<cstri ...

  9. 如何使用Django 启动命令行及执行脚本

    使用django启动命令行和脚本,可以方便的使用django框架做开发,例如,数据库的操作等. 下面分别介绍使用方法. django shell的启动 启动命令: $/data/python-virt ...

  10. asp.net 退出登陆(解决退出后点击浏览器后退问题仍然可回到页面问题)

    代码如下: Session.Abandon(); Response.Redirect("Login.aspx"); 但是这样点点击浏览器的后退仍然可以回到刚才的页面,这可不行,在网 ...