什么是装饰器(decorator)
简单来说,可以把装饰器理解为一个包装函数的函数,它一般将传入的函数或者是类做一定的处理,返回修改之后的对象.所以,我们能够在不修改原函数的基础上,在执行原函数前后执行别的代码.比较常用的场景有日志插入,事务处理等.
装饰器
最简单的函数,返回两个数的和
-
def calc_add(a, b):
-
return a + b
-
-
calc_add(
1,
2)
但是现在又有新的需求,计算求和操作耗时,很简单,求和前获取一下时间,求和后再获取一次,求差即可
-
import datetime
-
-
def calc_add(a, b):
-
start_time = datetime.datetime.now()
-
result = a + b
-
end_tiem = datetime.datetime.now()
-
print
“result:”, result,
“used:”, (end_tiem - start_time).microseconds,
“μs”
-
return result
-
-
calc_add(
1,
2)
现在呢,函数calc_diff(a, b),计算a-b,也想计算减法操作的时间差,很好办,把那段代码复制过去.但是假如我们现在想编的是一个数学函数库,各种函数都想计算其执行耗时,总不能一个一个复制代码,想个更好的办法.
我们知道,在Python中函数也是被视为对象的,可以作为参数传递,那么假如把计算耗时的独立为一个单独的函数calc_spend_time(),然后把需要计算耗时的函数例如calc_add的引用传递给它,在calc_spend_time中调用calc_add,这样所有的需要计算耗时的函数都不用修改自己的代码了.
-
def calc_spend_time(func, *args, **kargs):
-
start_time = datetime.datetime.now()
-
result = func(*args, **kargs)
-
end_tiem = datetime.datetime.now()
-
print
“result:”, result,
“used:”, (end_tiem - start_time).microseconds,
“μs”
-
-
-
def calc_add(a, b):
-
return a + b
-
-
calc_spend_time(calc_add,
1,
1)
-
# calc_spend_time(calc_add, a=1, b=2)
看起来也不错,负责计算的函数不用更改,只需调用的时候作为参数传给计算时间差的函数.但就是这,调用的时候形式变了,不再是clac(1, 2),而是calc_spend_time(clac_add, 1, 2),万一calc_add大规模被调用,那么还得一处一处找,然后修改过来,还是很麻烦.如果想不修改代码,就得使clac()和calc_spend_time(clac)效果一样,那么可以在calc_spend_time()里把传入的clac包装一下,然后返回包装后的新的函数,再把返回的包装好的函数赋给clac,那么calc()的效果就和上例calc_spend_time(calc())效果一样.
-
import datetime
-
-
def calc_spend_time(func):
-
def new_func(a, b):
-
start_time = datetime.datetime.now()
-
result = func(a, b)
-
end_tiem = datetime.datetime.now()
-
print
“result:”, result,
“used:”, (end_tiem - start_time).microseconds,
“μs”
-
return new_func
-
-
def calc_add(a, b):
-
return a + b
-
-
calc_add = calc_spend_time(calc_add)
-
calc_add(
1,
2)
语法糖
上面的例子就是装饰器的概念,包装函数的函数.事实上上面的例子还可以更精简
-
import datetime
-
-
def calc_spend_time(func):
-
def new_func(a, b):
-
start_time = datetime.datetime.now()
-
result = func(a, b)
-
end_tiem = datetime.datetime.now()
-
print
“result:”, result,
“used:”, (end_tiem - start_time).microseconds,
“μs”
-
return new_func
-
-
@calc_spend_time
-
def calc_add(a, b):
-
return a + b
-
-
calc_add(
1,
2)
@calc_spend_time就是语法糖,它的本质就是:calc_add = calc_spend_time(calc_add)
无参数的函数装饰器
-
import datetime
-
-
def calc_spend_time(func):
-
def new_func(*args, **kargs):
-
start_time = datetime.datetime.now()
-
result = func(*args, **kargs)
-
end_tiem = datetime.datetime.now()
-
print
“result:”, result,
“used:”, (end_tiem - start_time).microseconds,
“μs”
-
return new_func
-
-
@calc_spend_time
-
def calc_add(a, b):
-
return a + b
-
-
-
@calc_spend_time
-
def calc_diff(a, b):
-
return a - b
-
-
calc_add(a=
1, b=
2)
-
calc_diff(
1,
2)
注:
- *args:把所有的参数按出现顺序打包成list
- **kargs:把所有的key=value形式的参数打包成一个dict
带参数的函数装饰器
假如我们需要知道函数的一些额外信息,例如函数作者,可以通过给装饰器函数增加参数来实现.
-
import datetime
-
-
def calc_spend_time(author):
-
def first_deco(func):
-
def new_func(*args, **kargs):
-
start_time = datetime.datetime.now()
-
result = func(*args, **kargs)
-
end_tiem = datetime.datetime.now()
-
print author,
“result:”, result,
“used:”, (end_tiem - start_time).microseconds,
“μs”
-
return new_func
-
return first_deco
-
-
-
@calc_spend_time(‘author_1’)
-
def calc_add(a, b):
-
return a + b
-
-
-
@calc_spend_time(‘author_2’)
-
def calc_diff(a, b):
-
return a - b
-
-
calc_add(a=
1, b=
2)
-
calc_diff(
1,
2)
Python内置装饰器
Python内置的装饰器有三个:staticmethod,classmethod和property.
- staticmethod:把类中的方法定义为静态方法,使用staticmethod装饰的方法可以使用类或者类的实例对象来调用,不需要传入self
-
class Human(object):
-
“”“docstring for Human”“”
-
def __init__(self):
-
super(Human, self).__init__()
-
-
@staticmethod
-
def say(message):
-
if
not message:
-
message =
‘hello’
-
print
‘I say %s’ % message
-
-
def speak(self, message):
-
self.say(message)
-
-
Human.say(
None)
-
human = Human()
-
human.speak(
‘hi’)
输出:
-
I
say hello
-
I
say hi
- classmethod:把类中的方法定义为类方法,使用classmethod装饰的方法可以使用类或者类的实例对象来调用,并将该class对象隐式的作为第一个参数传入
-
class Human(object):
-
"""docstring for Human"""
-
def __init__(self):
-
super(Human, self).__init__()
-
self.message =
'111'
-
-
def say(message):
-
if
not message:
-
message =
'hello'
-
print
'I say %s' % message
-
-
@classmethod
-
def speak(cls, message):
-
if
not message:
-
message =
'hello'
-
cls.say(message)
-
-
-
human = Human()
-
human.speak(
'hi')
输出同上例
- property:把方法变成属性
-
class Human(object):
-
"""docstring for Human"""
-
def __init__(self, value):
-
super(Human, self).__init__()
-
self._age = value
-
-
@property
-
def age(self):
-
return self._age
-
-
human = Human(
20)
-
print human.age
</div>
</div>