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

由浅入深分析python修饰器

2016-11-10 21:47 363 查看
单例模式的设计中有种方法,通过修饰器设计:

def singleton(cls, *args, **kw):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return getinstance

@singleton
class MyClass:
...


复习加深入思考单例模式的设计思路与原理

一、为什么要使用修饰器

先打一个比方,一个玻璃杯子,我们用来装水(功能),有一天,我们想装热水(增强后的功能),那我们需要在杯子外面套一个皮套(额外的功能),这时,皮套就起到了装饰器的功能,他提供了隔热的效果,使杯子有了装热水的功能。而且,这个皮套能够通用的套在不同的杯子上,达到同样的效果。

回到我们的问题,装饰器根本上也是一种方法,他形式上简单易用,可以反复应用在不同的函数上,比如:输出日志,打印调用时间。装饰器在一定程度上提取了复用的代码,增强原函数的功能。

分析完了为什么我们使用修饰器,我们现在从头开始分析修饰器是怎样形成的。

二、由浅入深分析修饰器的形成

1,现在,我们有一个函数 hello

def hello():
print 'hello world'


2,我们想添加一个功能,让他打印调用的时间,很简单,加入一行代码就够了(为了简单,本文省略import time语句)

def hello():
print time.time()
print 'hello world'


3,我们的效果达到了,但是,如果有fun1,fun2,fun3也需要达到同样的效果,我们难道要每个函数都修改?这显然增加了毫无意义的劳动。我们何不专门定义一个函数,用来打印调用时间并执行函数

def call_time(fun):
print time.time()
hello()

def hello(): print 'hello world'

call_time(hello)


4,现在虽然目的达到了,但是如果在程序中显然更改了业务流程,本来调用hello函数,却变成了调用call_time函数(如果hello函数有参数,显然这种方法不可取)

5,我们可以定义一个函数,其功能为返回增强后的功能

def call_time(fun):
def wrapper():
print time.time()
return hello() #这里添加return,如果hello有返回值则将其返回
return wrapper

def hello(): print 'hello world'

hello = call_time(hello)
hello()


python中定义了修饰器的语法规则,上述代码等同于:
def call_time(fun):
...

@call_time
def hello(): print 'hello world'

hello()


6,如果我们的hello函数有一个参数,输入一个姓名,输出打招呼

def hello(str):
print 'hello %s'%str


这是我们修改一下代码,添加接收参数,一个简单的修饰器就形成了

简单的修饰器

def call_time(fun):
def wrapper(*args, **kw):
print time.time()
return hello(*args, **kw)
return wrapper

@call_time
def hello(str): print 'hello %s'%str

hello('TimorChow')


带参的修饰器

有时候我们需要给修饰器的功能传递一些参数,只要在现有的修饰器外加装一层接收参数的函数就好了

def call_time_args(str):
def call_time(fun):
def wrapper(*args, **kw):
print str,time.time()
return hello(*args, **kw)
return wrapper
return call_time

@call_time_args('now time')
def hello():
print 'hello'

hello()


结果为:

now time:1468135648943

hello

类装饰器

再来看看类装饰器,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

class Foo(object):
def __init__(self, func):
self._func = func

def __call__(self):
print ('class decorator runing')
self._func()
print ('class decorator ending')

@Foo
def bar():
print ('bar')

bar()


functools.wraps

使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、name、参数列表,先看例子:

装饰器

def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging


函数

@logged
def f(x):
"""does some math"""
return x + x * x


该函数完成等价于:

def f(x):
"""does some math"""
return x + x * x
f = logged(f)


不难发现,函数f被with_logging取代了,当然它的

docstring,__name__就是变成了with_logging函数的信息了。

print f.__name__    # prints 'with_logging'
print f.__doc__     # prints None


这个问题就比较严重的,好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。

from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ + " was called"
return func(*args, **kwargs)
return with_logging

@logged def f(x): """does some math""" return x + x * x
print f.__name__ # prints 'f'
print f.__doc__ # prints 'does some math'


内置
@staticmathod、@classmethod、@property
的顺序

@a
@b
@c
def f ():


等效于

f = a(b(c(f)))


参考文章:https://www.zhihu.com/question/26930016/answer/99243411
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python