Python中的装饰器可以在不修改原始函数代码的基础上,在Python函数中插入一些额外操作
简单装饰器
装饰器定义
1
2
3
4
5def decorator(func):
def wrapper(*args, **kwargs):
print "decorator"
return func(*args, **kwargs)
return wrapper装饰器使用
不带参数的函数
1
2
3
4
5
6
7
8
9
10@decorator
def test():
print "inner test"
# just like a normal function
test()
# output:
decorator
inner test带参数的函数
1
2
3
4
5
6
7
8
9
10@decorator
def add(a, b):
print "sum: ", a+b
# just like a normal function
add(10, 20)
# output:
decorator
sum: 30
装饰器是一种语法糖
- 实际上上面的代码等价于
1
2
3
4
5
6
7
8
9
10
11def test():
print "inner test"
test = decorator(test)
# test is a normal
test()
# output:
decorator
inner test
带参数的装饰器
- 需要对装饰器进一步的封装
1 | def outterDecorator(tag): |
* 在原始的装饰器外面封装一层函数,用于接受参数,其他的不用改变
- 理解:
- 等价于给装饰器加了一层接受参数的外层空间
- 实际上调用的时候除了参数外,其他的都没变
- 被装饰的函数依然是被作为内层函数的参数传入装饰器中
类装饰器
1 | class Foo(object): |
- 如上述代码所示,类装饰器必须有
__init__
和__call__
两个函数 __init__
负责接受被装饰函数作为参数并存储该函数__call__
负责执行函数调用过程并执行想要插入函数的代码- 被装饰的函数被调用时本质上是
__call__
函数被调用
类装饰器的优点
- 灵活度高
- 高内聚,不像函数一样定义在外面
- 封装的好,容易阅读
多个装饰器的顺序问题
1 | @a |
- 函数可以同时被多个装饰器修饰
- 装饰器的顺序从靠近函数的那个开始从内向外一层层封装
1
f = a(b(c(f)))
装饰器对原始函数的属性修改
涉及到
docstring
,__name__
等属性1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 装饰器
def logged(func):
def with_logging(*args, **kwargs):
print func.__name__ # 输出 'with_logging'
print func.__doc__ # 输出 None
return func(*args, **kwargs)
return with_logging
# 函数
@logged
def f(x):
"""does some math"""
return x + x * x
logged(f)使用
functools.warps
装饰器可以修复原始函数的文档1
2
3
4
5
6
7
8
9
10
11
12
13from functools import wraps
def logged(func):
@wraps(func)
def with_logging(*args, **kwargs):
print func.__name__ # 输出 'f'
print func.__doc__ # 输出 'does some math'
return func(*args, **kwargs)
return with_logging
@logged
def f(x):
"""does some math"""
return x + x * x
property装饰器
用于类的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Student(object):
def __init__(self, birth):
self._birth = birth
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2014 - self._birth:当加上
property
装饰器后,函数就变成了一个只读属性,被修饰的函数不能再当成普通函数当前函数不能有参数,除非是默认参数,因为当前函数变成属性后,直接调用
1
s.birth(10)
- 解析是
s.birth
返回一个属性值,然后,属性值不能被调用,所以抛出异常
- 解析是
property
装饰器会生成两个新的装饰[method_name].setter
和[method_name].getter
,分别用于代表当前函数对应属性的的写和读功能,读的功能默认加上了,写的功能需要的话我们可以使用[method_name].setter
装饰器实现总结:
property
装饰器可以将类的某个属性封装起来(在不暴露类属性的情况下提供getter
方法和setter
方法(后者需要自己显示定义))