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

Python装饰器

2015-10-13 21:38 405 查看

python装饰器

首先要明白的一点是,装饰器装饰的是函数。python之中为什么会有这样的需求?

需求是怎么来的

def foo():
print 'in foo()'

foo()


这是一个函数,如果需要对这个函数增加一些功能,比如需要计算这个函数运行所消耗的时间。

import time
def foo():
start = time.clock()
print 'in foo()'
end = time.clock()
print 'used:', end - start

foo()


如果其他函数也需要计算运行时间呢?复制代码?DRY,所以可以将计算函数运行时间的功能抽象出来。

import time

def foo():
print 'in foo()'

def timeit(func):
start = time.clock()
func()
end =time.clock()
print 'used:', end - start

timeit(foo)


嗯,看上去挺不错的。不过这里的弱点是修改了调用部分的代码,关键是有的时候如果调用的函数是库函数的话就麻烦了。

所以这个时候想到的就是对foo进行包装,将timeit编写为对foo的包装函数,传入foo之后对foo进行加强,返回一个新的foo。

#-*- coding: UTF-8 -*-
import time

def foo():
print 'in foo()'

# 定义一个计时器,传入一个函数,并返回另一个附加了计时功能的函数
def timeit(func):
# 定义一个内嵌的包装函数,给传入的函数加上计时功能的包装
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
# 将包装后的函数返回
return wrapper

foo = timeit(foo)
foo()


PS:在在这个例子中,函数进入和退出时需要计时,这被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。与传统编程习惯的从上往下执行方式相比较而言,像是在函数执行的流程中横向地插入了一段逻辑。在特定的业务领域里,能减少大量重复代码。

问题:如果foo带参数怎么办?

很简单,装饰器之中的包装函数的参数和被装饰的函数的参数需要统一。

def deco(func):
def _deco(a, b):
print("before myfunc() called.")
ret = func(a, b)
print("  after myfunc() called. result: %s" % ret)
return ret
return _deco

@deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a + b


再举一个例子是Django之中的Signal装饰器:

def receiver(signal, **kwargs):
def _decorator(func):
if isinstance(signal, (list, tuple)):
for s in signal:
s.connect(func, **kwargs)
else:
signal.connect(func, **kwargs)
return func
return _decorator


语法糖

Python对装饰器提供了语法糖:

import time

def timeit(func):
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
return wrapper

@timeit
def foo(): print 'in foo()' foo()


内置装饰器

内置的装饰器有三个,分别是staticmethod、classmethod和property。作用分别是把类中定义的实例方法变成静态方法、类方法和类属性。

class Rabbit(object):

def __init__(self, name):
self._name = name

@staticmethod
def newRabbit(name):
return Rabbit(name)

@classmethod
def newRabbit2(cls):
return Rabbit('')

@property
def name(self):
return self._name


示例比较简单,其中的函数是可以进行扩展的。这里@property定义的是一个只读属性,如果需要可写,则需要再一定一个setter:

@name.setter
def name(self,name):
self._name = name
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: