python 装饰器

来源:互联网 发布:科研数据分析 书 编辑:程序博客网 时间:2024/05/16 01:35

一、 装饰器

装饰器是在python 2.4 中新加入的,使得函数和方法封装更容易阅读和理解。

对一个函数进行封装,使得函数在调用时,完成一些额外的工作,类似于AOP。

在引入装饰器之前,语法如下:

class WhatFor(object):def it(cls):print 'work with %s' % cls#定义it为类方法it = classmethod(it)def uncommon():print 'I could be a global function'#定义uncommon为静态方法uncommon = staticmethod(uncommon)


有了装饰器后

class WhatFor1(object):@classmethoddef it(cls):print 'work with %s' % cls@staticmethoddef uncommon():print 'I could be a global function'



二、编写装饰器


编写装饰器最简单、最容易理解的方法:编写一个函数,返回封装了原始函数(被装饰的函数)一个子函数。


def mydecorator(fun):#子函数,封装原始函数def _mydecorator(*args, **kw):print 'before work'res = fun(*args, **kw)print 'after work'return res#返回子函数return _mydecorator@mydecoratordef func():print 'function work'func()


常见的装饰模式 或者说是功能有:

  • 参数检查
  • 缓存
  • 代理
  • 上下文提供者


三、 参数检查


在有些环境中,需要检查函数接收或者返回的参数,装饰器可以提供函数自省能力。

from itertools import iziprpc_info = {}def xmlrpc(in_ =(), out=(type(None),)):def _xmlrpc(function):def _xmlrpc(function):#注册签名func_name = function.func_namerpc_info[func_name] = (in_,out)def _check_types(elements, types):"""Subfunction that checks the types."""if len(elements) != len(types):raise TypeError('参数数量不对')typed = enumerate(izip(elements, types))for index, couple in typed:arg, of_the_right_type = coupleif isinstance(arg, of_the_right_type):continueraise TypeError('arg #%d should be %s' %(index, of_the_right_type)) #类型不对,抛出typeerror#封装函数def _xmlrpc(*args):#检查输入的内容checkable_args = args[1:]_check_types(checkable_args, in_)#执行函数res = function(*args)#检查输出的内容if not type(res) in (tuple, list):checkable_res=(res,)else:checkable_res=res_check_types(checkable_res, out)return resreturn _xmlrpcreturn _xmlrpc#使用实例class RPCView(object):# 2个 int 输入, 没有返回@xmlrpc((int, int)) def meth1(self, int1, int2):print 'recieve %d and %d' % (int1, int2)# 一个str输入,一个int输出@xmlrpc((str,), (int,)) def meth2(self ,phrase):print 'recieve %s' % phrasereturn 12print rpc_infomy = RPCView()#传入参数合乎要求my.meth1(1,2)#传入参数类型不对,抛出类型错误my.meth2(2)



四、缓存


在要求高效的环境中,通常的优化手段是使用缓存。缓存装饰器把函数的传入参数和结果配对记录下来,下来调用函数时,先从记录中查找,直接返回。适用于输入参数值有限的环境。

import timeimport hashlibimport picklefrom itertools import chaincache = {}def is_obsolete(entry,duration):return time.time() - entry['time'] > durationdef compute_key(function, args, kw):key = pickle.dumps((function.func_name,args,kw))return hashlib.sha1(key).hexdigest()def memoize(duration=10):def _memoize(function):def _memoize(*args, **kw):key = compute_key(function, args, kw)#检查缓存中是否已经有了,而且没有过期if (key in cache and not is_obsolete(cache[key],duration)):print 'we got a winner'return cache[key]['value']#如果没有或者已过期,则计算并放入cache中result = function(*args,**kw)cache[key] = {'value':result,'time':time.time()}return resultreturn _memoizereturn _memoize@memoize()def very_very_very_complex_stuff(a,b):return a+bvery_very_very_complex_stuff(2,2)#返回的是缓存的结果very_very_very_complex_stuff(2,2)


五、代理

代理装饰器使用一个全局机制来标识和注册函数。例如:一个有权限保护的执行函数可以使用一个集中检测器和相关可调用对象权限来实现。
# -*- coding: UTF-8 –*-#用户类,包含权限class User(object):def __init__(self, roles):self.roles = roles#自定义异常class Unauthorized(Exception):passdef protect(role):def _protect(function):def __protect(*args, **kw):user = globals().get('user')if user is None or role not in user.roles:raise Unauthorized("I won't tell you")return function(*args, **kw)return __protectreturn _protect#角色1,拥有admin 和user两种权限tarek = User(('admin','user'))#角色2,只有user权限bill = User(('user'))class MySecrets(object):#只有admin权限才能执行此函数@protect('admin')def waffle_recipe(self):print 'use tons of butter'these_are = MySecrets()user = tarek#有权限,可以执行these_are.waffle_recipe()user = bill#没有权限,抛出异常these_are.waffle_recipe()


六、上下文提供者


这种装饰器用来确保函数可以运行在正确的上下文中,或者在函数前后执行一些代码。用来设置或复位特定的执行环境。
例如:当一个数据项必须与其他线程共享时,就需要一个锁来保护它。这个锁可以在一个装饰器中编写。


from threading import RLocklock = RLock()def synchronized(function):def _synchronized(*args, **kw):lock.acquire()try:return function(*args, **kw)finally:lock.release()return _synchronized@synchronizeddef thread_safe():passthread_safe()

上下文装饰器在2.5 以后可以使用with语句来代替。


0 0
原创粉丝点击