Python 装饰器
来源:互联网 发布:java获取访问者ip 编辑:程序博客网 时间:2024/06/18 15:03
原文链接:Python Decorator
Python有一个有趣的特征叫做装饰器,可以向已存在的代码添加功能。这也被称作元编程(metaprogramming, 编写操纵其它程序的程序,企图在运行时完成部分本应在编译时完成的工作)
Ref:metaprogramming
热身
为了理解装饰器,我们首先应该知道以下几点:
- 一切皆对象
- 我们所定义的名称仅仅是对象的标识
- 函数也是对象
- 不同的名称可以绑定到相同的对象
来看一个例子:
>>> first("Hello")Hello>>> second = first>>> second("Hello")Hello上面的例子中,first和second都引用了同一个函数对象。
现在事情开始变得复杂了,函数能够被当做一个参数传递给另一个函数。如果你以前用过map,filter,reduce,那么你可能对此已经有所了解。这些函数将其它函数作为参数,也被称作高阶函数(Higher order functions)。
下面是这类函数的几个例子:
def inc(x): '''Function to increase value by 1''' return x + 1def dec(x): '''Function to decrease value by 1''' return x - 1def operate(funct, x): '''A higher order function to increase or decrease''' result = func(x) return result像下面这样调用:
>>> operate(inc, 3)4>>> operate(dec, 3)2而且,一个函数还能返回另一个函数。
>>> def is_called():... def is_returned():... print('Hello')... return is_returned...>>> new = is_called()>>> new()Hello>>>这里,is_returned()是一个嵌套函数,每次我们调用is_called()的时候就返回这个函数。
函数或方法如果能够被调用,那么我们称之为可调用的(callable)。实际上,任何的对象只要实现了__call__()就被认为是可调用的。所以,大多数情况下,一个装饰器就是一个返回可调用对象的可调用对象(A decorator is a callable that returns a callable)。从根本上说,一个装饰器接收一个函数,添加一些功能然后返回,像下面这样:
>>> def is_called():... def is_returned():... print('Hello')... return is_returned...>>> new = is_called()>>> new()Hello>>> def make_pretty(func):... def inner():... print('I got decorated')... func()... return inner...>>> def ordinary():... print('I am ordinary')...>>> ordinary()I am ordinary>>> # 装饰ordinary函数>>> pretty = make_pretty(ordinary)>>> pretty()I got decoratedI am ordinary在上面的例子中,make_pretty()就是一个装饰器。在赋值的这一步
pretty = make_pretty(ordinary)函数ordinary()被装饰,返回的函数被赋予一个新的名字pretty。肯一看到,装饰器函数向原函数添加了一些新的功能。就像包装一个礼物一样,装饰器扮演了一个包装纸的角色。被包装对象(盒子里边的礼物)的本来的特性并没有改变,但是现在它变得更好看了(被装饰了)。
通常情况下,我们会装饰一个函数并这样赋值给它:
ordinary = make_pretty(ordinary)这是普遍的一种做法,Python有特定的语法来简化这种操作。我们可以使用@符号后门见跟上装饰器的名字,把它放在需要装饰的函数的定义上面,像这样:
@make_pretty def ordinary(): print("I am ordinary")等价于:
def ordinary(): print("I am ordinary") ordinary = make_pretty(ordinary)这仅仅是采用了语法糖来实现装饰器。
带参数的装饰函数
上面介绍的装饰器非常简单,它只适用于装饰不带参数的函数。要是我们想装饰下面这种函数该怎么办呢:
def divide(a, b): return a/b这个函数有两个参数,a和b。我们知道,如果我们传入b的值为0,函数将会抛出一个错误
>>> divide(2, 5)0.4>>> divide(2, 0)Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in divideZeroDivisionError: division by zero现在我们使用装饰器来检查这种情况:
def smart_divide(func): def inner(a, b): print("I am going to divide",a,"and",b) if b == 0: print("Whoops! cannot divide") return return func(a,b) return inner @smart_divide def divide(a, b): return a/b如果发生除0错误,新的实现将会返回None
>>> divide(2, 5)I am going to divide 2 and 50.4>>> divide(2, 0)I am going to divide 2 and 0Whoops! cannot divide使用这种方式我们可以装饰带参数的函数。细心的读者可能注意到嵌套函数inner()的参数列表跟被装饰对象的参数列表是一样的。考虑到这种情况,我们设计一个装饰器能够装饰带任意数量参数的函数。在Python中,function(*args, **kwargs)实现了这种魔法,其中args是非关键字参数(位置有序参数,positional arguments)的元组, kwargs是关键字参数的字典。看下面的一个例子:
def works_for_all(func): def inner(*args, **kwargs): print("I can decorate any function") return func(*args, **kwargs) return inner链式装饰器
在Python中可以链式的使用多个装饰器。即是说,一个函数可以被不同的函数装饰多次。仅仅把装饰器依次放在被装饰函数的上面就好了
def works_for_all(func): def inner(*args, **kwargs): print("I can decorate any function") return func(*args, **kwargs) return innerdef star(func): def inner(*args, **kwargs): print('*' * 30) func(*args, **kwargs) print('*' * 30) return innerdef percent(func): def inner(*args, **kwargs): print('%' * 30) func(*args, **kwargs) print('%' * 30) return inner@star@percentdef printer(msg): print(msg)
>>> printer('Hello')******************************%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Hello%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%******************************上面的语法,
@star@percentdef printer(msg): print(msg)等价于:
def printer(msg): print(msg)printer = star(percent(printer))在链式装饰中,装饰器的顺序是重要的,如果我们交换下装饰器的顺序:
@percent@stardef printer(msg): print(msg)结果会有所不同:
>>> printer('Hello')%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%******************************Hello******************************%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
使用装饰器的一个例子
使用装饰器来计算一个函数调用的时间消耗
from time import timedef timeit(func): '''Use this decorator to measure execution time of a function. eg. @timeit def yourFunction(args): dosomething... ''' def inner(*args, **kwargs): start = time() result = func(*args, **kwargs) elapsed = time() - start print('Function %s costs time : %10.6f seconds.' % (func.__name__, elapsed)) return result return inner@timeitdef testFunc(n): sum = 0 for i in range(n): sum += i * iif __name__ == '__main__': testFunc(100000000)
输出结果:
Function testFunc costs time : 18.431244 seconds.
0 0
- python装饰器装饰类
- [python]python装饰器
- Python装饰器学习
- Python装饰器
- python 装饰器
- Python装饰器
- python 装饰器 应用
- python 装饰器2
- Python装饰器学习
- python装饰器
- python装饰器
- python 装饰器入门
- python装饰器
- python装饰器
- python 装饰器'@'
- python装饰器
- Python装饰器详解
- python 装饰器
- 关于c++的测试
- 【spring框架】关于DataSource(下)
- 12169 - Disgruntled Judge
- IOS UI_视频播放器_MPMoviePlayerController
- sscanf()函数总结
- Python 装饰器
- 第3周项目4-考了语文数学的学生(多文件形式组织程序)
- android开发者的42个必读网站
- 十大Intellij IDEA快捷键
- java类初始化顺序 ----需要研究的
- Android SDK开发包国内下载地址
- 3.3计算字符串的相似度
- csu 1525: Algebraic Teamwork
- 关于GPIO