您的位置:首页 > 编程语言 > Python开发

python 装饰器

2014-04-27 12:51 183 查看

一、 装饰器

装饰器是在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):
@classmethod
def it(cls):
print 'work with %s' % cls

@staticmethod
def 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

@mydecorator
def func():
print 'function work'

func()


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

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

三、 参数检查

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

from itertools import izip

rpc_info = {}

def xmlrpc(in_ =(), out=(type(None),)):
def _xmlrpc(function):
def _xmlrpc(function):
#注册签名
func_name = function.func_name
rpc_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 = couple
if isinstance(arg, of_the_right_type):
continue
raise 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 res

return _xmlrpc

return _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' % phrase
return 12

print rpc_info

my = RPCView()

#传入参数合乎要求
my.meth1(1,2)

#传入参数类型不对,抛出类型错误
my.meth2(2)


四、缓存

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

import time
import hashlib
import pickle
from itertools import chain

cache = {}

def is_obsolete(entry,duration):
return time.time() - entry['time'] > duration

def 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 result

return _memoize

return _memoize

@memoize()
def very_very_very_complex_stuff(a,b):
return a+b

very_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):
pass

def 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 __protect
return _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 RLock

lock = RLock()

def synchronized(function):
def _synchronized(*args, **kw):
lock.acquire()

try:
return function(*args, **kw)

finally:

lock.release()
return _synchronized

@synchronized
def thread_safe():
pass

thread_safe()

上下文装饰器在2.5 以后可以使用with语句来代替。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: