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

python深入学习--decorator强大的装饰器

2015-04-14 20:10 561 查看
欢迎转载,转载请注明原文地址:/article/8326762.html

一.decorator基础。

最初接触python是在大学毕业进入某一游戏公司后,最开始觉得python不严谨,很不喜欢python这种“简单”的脚本,但是后来,越来越离不开python了,觉得这么灵活方便的语言,简直是程序员的福音,尤其是它的数据结构,他带来了一个“{}” 大括号就能搞定一切的时代。当然python还有很多优秀的特性。现在我们来讲python的特性之一--decorator(装饰器)。

装饰器,顾名思义就是修饰函数或者类的,它把要装饰的函数作为参数,然后在函数体内可以执行很多灵活的操作。这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用。装饰器的语法以@开头,接着是装饰器函数的名字和可选的参数。跟着装饰器声明的是被修饰的函数,和装饰函数的可选参数。装饰器看起来会是这样:

@decorator()
def func(*args):
pass

func(*args)


其解释器会解释成下面这样的语句:

func = decorator(func)
func(*args)
其实,写过python的人都接触过@decorator。比如写一个单例类:

class Singleton(object):
_instance = None
def __init__(self):
print "init single"

@classmethod
def instance(cls):
if cls._instance is None:
cls._instance = Singleton()
return cls._instance

def func(self):
print "call func"


@classmethod就是一个decorator,是python内置的装饰器(还有@staticmethod...)

class Singleton(object):
_instance = None
def __init__(self):
print "init single"

#@classmethod
def instance(cls):
if cls._instance is None:
cls._instance = Singleton()
return cls._instance

instance = classmethod(instance)

def func(self):
print "call func"

Singleton.instance().func()
以上跟这个效果是一样的。只不过却让instance少写了两次。

二.decorator的用法。

1.修饰函数:

decorator修饰函数有几种用法。

1).单个Decorator,不带参数

def deco(func):
def call_method(*args):
print "before call method"
func(*args)
print "after call method"
return call_method

@deco()
def func(a,b):
print "in func"
return a + b

func(1,2)


输出为:
before call method

in func

after call method

以上对于func的调用 跟以下是一样的(注释掉@deco()):

func = deco(func)
func(1,2)

再写的明白点:

call_method = deco(func) #deco这个函数返回call_method

call_method(*args) # 是不是恍然大悟了。

当然,最初的写法是最简单的,它让你每一个调用func()的地方都自动调用了@deco(),然后执行一些额外的操作。比如 print "after call method".当然你可以做你想做的任何事,比如在这里记log,你就不需要边CTRL+C,CTRL+V在每一个调用func的地方加上相同的代码了。

2).单个decorator,带参数。

def deco(a):
def real_deco(func):
def call_method(*args):
print "before call method"
result = a + func(*args)
print "after call method ",result
return call_method
return real_deco

@deco("Hello ")
def func(name):
print "in func ",name
return name

func("World!")
func("No World!")
输出为:

before call method

in func World!

after call method Hello World!

before call method

in func No World!

after call method Hello No World!

总结:

无参数decorator:把函数名传进去,然后生成一个新的装饰器函数

有参decorator:有参装饰,装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰

3).decorator还可以有多个,如下图所示:

@dec_a
@dec_b
@dec_c
def method(args):
pass
print "=========="
method = dec_a(dec_b(dec_c(method)))


只不过,平时项目中极少用到,我也就不讲了。

那么什么是装饰器?

现在我们知道装饰器实际就是函数。我们也知道他们接受函数对象。但它们是怎样处理那些函数的呢?一般说来,当你包装一个函数的时候,你最终会调用它。最棒的是我们能在包装的环境下在合适的时机调用它。我们在执行函数之前,可以运行些预备代码,也可以在执行代码之后做些清理工作。所以当你看见一个装饰器函数的时候,很可能在里面找到这样一些代码,它定义了某个函数并在定义内的某处嵌入了对目标函数的调用或者至少一些引用。从本质上看,这些特征引入了java
开发者称呼之为AOP(Aspect Oriented Programming,面向方面编程)的概念。

你可以考虑在装饰器中置入通用功能的代码来降低程序复杂度。例如,可以用装饰器来:

引入日志

增加计时逻辑来检测性能

给函数加入事务的能力

2.修饰类:

修饰类和修饰函数差不多,修饰类是把@decorator放在class上面,用以修改类的属性

<span style="font-size:14px;">def func(self):
return "func"

def deco(kclass):
kclass.func = func
kclass.name = "Hello"
return kclass

@deco
class test(object):
def __init__(self):
print "in init",self.name
print "call func: ",self.func()

test()</span>


输出:

in init Hello

call func: func

三.项目实践

1.最近项目中用到decorator比较多。其中有修饰类的:

def is_persistent(_self):
return True

def Persistent(klass):
"""
类的decorator,用来修饰Entity的子类。如:
@Persistent
class player(Entity):
...
这样的类才会被序列化到mongodb中
"""
klass.is_persistent = is_persistent
return klass

@Persistent
class player(Entity):
pass
相当于在 player里面定义了一个 is_persistent()的方法。在需要Persistent的地方加上@Persistent就行了,当然也可以在每个需要@Persistent的类里面重写这个方法,但是当对象多的时候,自己也不知道自己写了没有了。

2.用在callback里面,但是callback的参数有的存在,有的还需要经过复杂的操作获取,假如你要经过一个异步的操作,操作回来调用func(requestId,*args),requestId是已知的,但是*args是回调回来才有的,你肯定有地方可以存requestId,然后回调回来,再取值进行func操作,但是使用类似decorator的操作可以简化这一操作:

<span style="font-size:14px;">def request_decorator(requestId, func):
"""
传递请求序号参数,用于回调
:param func:
:return:
"""
def requestId_callback(*args):
return func(requestId, *args)
return requestId_callback

callback = request_decorator(requestId, self.call_func)
...
args = ...
callback(*args)</span>


巧妙的把已有的参数传进去,然后等其他参数回来就可以了。

3.使用decorator来增加参数:

在上一篇lambda的用法 /article/8326766.html 中讲解了用lambda来增加参数的例子。

def addBtnClickHander(btn,func):
# 这个是通用接口,不可修改

def hander():
widget = "%s_name"%btn  # 根据btn取得
func(widget)

return hander

def main():
btn = 'btn'
name = 'name'
addBtnClickHander(btn,lambda widget:onClick(widget,name))

def onClick(widget,name):
print "onClick:",widget,name


我们这里用decorator来解决

def request_decorator(func,name):
"""
传递请求序号参数,用于回调
:param func:
:return:
"""
def requestId_callback(*args):
return func(name,*args)
return requestId_callback

def main():
btn = 'btn'

callback = request_decorator(onClick,'a')
addBtnClickHander(btn,callback)

def onClick(name,widget):
print "onClick:",widget,name


为什么可以这样?

addBtnClickHandler里面的func其实就是callback,func(widget)就是requestId_callback(*args),widget就是*args,那么我们要做的就是把name传进去,

return func(name,*args),就是调用了onClick(name,widget),明白了吧。

也许你会说,做出来了之后解释,大家都可以解释通,那么做之前怎么想到这么decorator可以满足要求呢,那么我们反着来,怎么设计decorator能增加参数,我们要改变的就是addBtnClickHandler里面的func,现在它只有一个参数,而我们需要两个(或者多个),那么就需要外界传进来,那么怎么传进来能满足要求呢

name = 'a'
func = onClick
def callback(widget):
return func(widget,name)


我们要的不就是这个嘛,func就是onClick,callback = addBtnClickHandler的func,但是我们addBtnClickHandler的参数是固定的,怎么传name进来呢,decorator,对,用它来包裹起来传进来,

def decorator(func,name):
def callback(widget):
return func(widget,name)
return callback
就像这样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: