python decorator

python中decorator使用形式基本上分为两种:

1.                                                               |  2.

@dec_no_arguments                                |  @dec_with_arguments(arg)

def f(*args, **kwargs):                                |  def f(*args, **kwargs):

    pass                                                       |      pass

第一种使用方式是不带参数的装饰器,第二种是带参数的装饰器。先说不带参数的

装饰器。

example1:

  1 #coding: utf-8
  2 
  3 import time
  4 import traceback
  5 import urllib2
  6 
  7 def timeit(func):
  8     def __(*args, **kwargs):
  9         t1 = time.time()
 10         try:
 11             ret = func(*args, **kwargs)
 12         except Exception, e:
 13             print tracebak.format_exc()
 14             return ''
 15         else:
 16             return ret
 17         finally:
 18             t2 = time.time()
 19             print 'call the function:%s spend time:%d ms' % (func.__name__, int((t2-t1) * 1000))
 20             return ret
 21     return __
 22 
 23 @timeit
 24 def read_from_net(url):
 25     return urllib2.urlopen(url).read()
 26 
 27 if __name__ == '__main__':
 28     ret = read_from_net('http://www.baidu.com')
 29     #do-something......</span>
第23行代码使用了无参数的timeit装饰器函数。python解释器在转化无参数的decorator

时使用如下的形式:

origin_function_name = decorator(origin_function_name)

origin_function_name是被装饰的函数的名称,直白点说就是@符号所在行的下一行的函

名称。在example 1中就是read_from_net函数,decorator就是@符号后面紧跟着的所有

内容。在example 1中就是timeit函数。一个值得注意的点是,这个转化是在python导入的

时候发生的而非代码在执行的时候才转化,如下例子就是一个很好的说明。

example 2 (decorator.py):

  1 #coding: utf-8
  2 
  3 import time
  4 import urllib2
  5 
  6 def timeit(func):
  7     print 'this is in the timeit function'
  8     def __(*args, **kwargs):
  9         t1 = time.time()
 10         ret = func(*args, **kwargs)
 11         t2 = time.time()
 12         print 'call the function:%s spend time:%d ms' % (func.__name__, int((t2-t1) * 1000))
 13         return ret
 14     return __
 15 
 16 @timeit
 17 def read_from_net(url):
 18     return urllib2.urlopen(url).read()
 19 
 20 if __name__ == '__main__':
 21     pass</span>
在终端中执行python decorator.py,输出如下:

this is in the timeit function

代码中并没有调用read_from_net函数,但是装饰器函数(这里指timeit函数)却被执行了,也就

是说转化发生在导入的时候。

当然装饰器不仅仅可以是函数,也可以是类,将上面的timeit代码改写成类,如下代码:

example 3:

  1 #coding: utf-8
  2 
  3 import time
  4 import urllib2
  5 
  6 def timeit(func):
  7     print 'this is in the timeit function'
  8     def __(*args, **kwargs):
  9         t1 = time.time()
 10         ret = func(*args, **kwargs)
 11         t2 = time.time()
 12         print 'call the function:%s spend time:%d ms' % (func.__name__, int((t2-t1) * 1000))
 13         return ret
 14     return __
 15 
 16 class TimeIt(object):
 17     def __init__(self, func):
 18         self.func = func
 19     def __call__(self, *args, **kwargs):
 20         t1 = time.time()
 21         ret = self.func(*args, **kwargs)
 22         t2 = time.time()
 23         print 'call the function:%s spend time:%d ms' % (self.func.__name__, int((t2-t1) * 1000))
 24         return ret
 25 
 26 @TimeIt
 27 def read_from_net(url):
 28     return urllib2.urlopen(url).read()
 29 
 30 if __name__ == '__main__':
 31     ret = read_from_net('http://www.baidu.com/')
 32     #do-something
TimeIt和timeit函数的装饰功能是一样的,只不过实现的方式不一样而已。使用类作为装 饰器

同样也遵守了上面提到的转化形式。

origin_function_name = decorator(origin_function_name) 

origin_function_name就是read_from_net

decorator是TimIt,替换相应的名称就是:read_from_net = TimeIt(read_from_net),竟然

毫无违和感,只是现在read_from_net是一个类了而已。read_from_net的调用方式并没有发

生任何的改变,因为类实现了__call__函数,这样对象也可以像正常的函数一样使用了。python

标准模块functools中的partial就是一个类作为装饰器,functools相关功能说明的链接是https://docs.python.org/2/library/functools.html

说完了不带参数的装饰器,下面该说带参数的装饰器了。

example 4:

#coding: utf-8

import time
import urllib2
import traceback

ALLOW_IP_LIST = ('127.0.0.1', '192.168.*.*, 172.10.100.*')

class IPError(Exception):
    pass

def check_ip(allow_ip_list=None):
    allow_ip_list = allow_ip_list or ALLOW_IP_LIST 
    def _dec1(func):
        def __dec2(self, *args, **kwargs):
            print '__dec2.__dict__', __dec2.__dict__
            cli_ip_frag = self.ip.split('.')
            for ip in allow_ip_list:
                ip_frag = ip.split('.')
                for i in range(len(ip_frag)):
                    if ip_frag[i] == '*':
                        continue
                    if ip_frag[i] != cli_ip_frag[i]:
                        break
                else:
                    return func(self, *args, **kwargs)
            raise IPError, self.ip
        return __dec2
    return _dec1

class CheckIp(object):
    def __init__(self, allow_ip_list=None):
        self.allow_ip_list = allow_ip_list or ALLOW_IP_LIST

    def __call__(self, func):
        allow_ip_list = self.allow_ip_list
        def __dec1(self, *args, **kwargs):
            cli_ip_frag = self.ip.split('.')
            for ip in allow_ip_list:
                ip_frag = ip.split('.')
                for i in range(len(ip_frag)):
                    if ip_frag[i] == '*':
                        continue
                    if ip_frag[i] != cli_ip_frag[i]:
                        break 
                else:
                    return func(self, *args, **kwargs)
            raise IPError, self.ip
        return __dec1

class Ping(object):
    def __init__(self, ip=None):
        self.ip = ip or ALLOW_IP_LIST[0]

    @check_ip()
    def get(self):
        return 'OK'

    @CheckIp()
    def post(self):
        return 'OK'

if __name__ == '__main__':
    p1 = Ping()
    print '.......................p1.get.....................'
    print 'p1.get', p1.get()
    print '......................p1.post....................'
    print 'p1.post', p1.post()
    p2 = Ping(ip='10.25.10.1')
    try:
        print '...................p2.get..................'
        p2.get()
    except IPError, e:
        print traceback.format_exc() 
    finally:
        print '................p2.post...................'
        p2.post()
<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: rgb(255, 255, 255);">在终端中执行输出的结果为:</span>

.......................p1.get.....................
p1.get OK
......................p1.post....................
p1.post OK
...................p2.get..................
Traceback (most recent call last):
  File "decorator.py", line 69, in <module>
    p2.get()
  File "decorator.py", line 25, in __dec2
    raise IPError, self.ip
IPError: 10.25.10.1


................p2.post...................
Traceback (most recent call last):
  File "decorator.py", line 74, in <module>
    p2.post()
  File "decorator.py", line 45, in __dec1
    raise IPError, self.ip
__main__.IPError: 10.25.10.1
example 4中使用了带参数的装饰器,分别用类和函数实现了装饰器。python在转化装饰器时

使用的形式和无参数形式是一样的。
origin_function_name = decorator(origin_function_name)

就拿get方法举例:

origin_function_name 是get,

decorator是check_ip(allow_ip_list)注意不是check_ip,这里的decorator正如刚开始描述的:

是@符号后面所有的内容,而这里@符号后面的是check_ip()而非check_ip。

所以替换后表达式如下:

get = check_ip()(get)

一般而言,对于一个被装饰器装饰的函数,在python解释器导入阶段就进行了转化,转化

之后的对象是否可调用,完全在于你的装饰器怎么实现。按照函数执行的观点来解释则简

单了很多,比如get方法的装饰,转化后是get = check_ip()(get),按照函数一个一个的调用

check_ip()返回了__dec1,则进一步有get = __dec1(get),然后再次执行__dec1(get)函

数的返回结果get = __dec2到这一步就是个普通的赋值了,那么get此时其实就是__dec2

只不过在转化的过程中,有些参数被保留在了闭包里,这样,直接调用get的时候,相当于执

行了闭包中的代码,对于一些共同的逻辑就能够提炼成一个decorator,方便使用,且使得代

码更加的简洁。

在使用check_ip装饰器的时候,注意不能写成@check_ip,即使不需要参数,check_ip后面的

括号还是需要的,不然转换就成这样的了:

get = check_ip(get) 转化之后get = _dec1

虽然这是合法的,调用的时候也没语法错误,但是显然不是我们期望的!为什么能成功呢,我们

看一下就知道了,get被转化成了_dec1函数,但我们使用p1.get()的时候,其实调用了__dec1函

,而__dec1函数刚好只有一个参数self,所以代码顺利的执行了,但是实际get的代码体没执行。

这个例子貌似很巧啊。

类和普通函数都能作为装饰器,个人更喜欢使用类做装饰器,感觉更清晰,特别是对于有参数的

装饰器,里面套了三层,简直是有点晕啊,使用类,嵌套貌似少了一层。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值