函数:python中的一等对象
函数也是对象
要想理解装饰器的语法、原理以及运行过程,首先要理解一个概念:在python中,函数是一等对象。
所谓一等对象是指满足一下条件的实体:在运行时创建
能赋值给变量或数据结构中的元素
能作为参数传给函数
能作为函数的返回结果
在Java或者C++中, 整数、字符、数组都是一等对象,这些在python中也不例外,但是在python里函数也是一等对象,同样满足以上条件,因此常被人简称“一等函数”。
举个例子1
2
3
4
5
6
7
8
9
10
11def ():
print('hello world!')
foo()
hello world!
type(foo) # 2
function
foo.__name__ #3
'foo'
以上三部分代码分别表示:foo是一个函数,可以调用
foo是function类的一个实例
foo具有一个属性,名为__name__,即函数名
用函数来处理函数
既然函数也是一等对象,那么既然函数可以用来处理基本类型,自然也就可以用来处理函数。
举个例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18def f():
... '''
... f is a function
... '''
... pass
f.__doc__ # 2
'ntf is a functionnt'
>>>def re_doc(func, doc): # 3
... func.__doc__ = doc
... return func
>>>f = re_doc(f, 'do nothing') # 4
>>> f.__doc__ # 5
'do nothing'定义一个函数f,以及函数说明__doc__
函数说明
定义一个函数,功能是修改一个函数的__doc__属性,参数是一个函数和一个字符串,返回值是一个函数
调用re_doc
f.__doc__被修改了
用函数来处理函数,就是这么简单。
函数内定义函数
既然可以函数内部定义一个变量,那么同样作为python中的一等对象,函数也可以在函数的内部定义1
2
3
4
5
6
7
8
9
10
11
12
13>>>def outer():
... a = 0
...
... def inner(): # 2
... print('inner function')
... inner() # 3
... print('outer function')
>>>outer()
inner function
outer function在函数内部定义一个变量a
在函数内部定义一个函数inner
调用inner
装饰器:用函数修改函数
假如现在有一个需求,需要函数运行结束后打印其运行时间,那么传统的做法又两种,一种是修改函数内部的代码,加入时间统计,二是在调用函数的地方统计。1
2
3
4
5
6
7# 未修改的代码
func1()
func2()
func3()
且不讨论实现这两种方式会影响到代码本身的结构,如果要统计的函数很多,那绝对是一场灾难。1
2
3
4
5
6
7
8
9
10
11
12
13
14# 修改后的代码
start_time = time.time()
func1()
print(time.time() - start_time)
start_time = time.time()
func2()
print(time.time() - start_time)
start_time = time.time()
func3()
print(time.time() - start_time)
既然是修改函数的功能,那我们可以尝试像对待普通变量一样,用函数来处理,也就是今天要讨论的主题——装饰器。
装饰器是一种特殊的函数,可以动态的调整函数(function)、方法(method)和类(class)的功能,而不需要修改其源代码或定义子类。
写一个装饰器
现在我们使用装饰器来实现上述需求1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17>>>import time
>>>def timeit(func):
... def wrapper(*args, **kwargs): # 2
... start_time = time.time()
... result = func(*args, **kwargs)
... print('' %(func.__name__, time.time() - start_time))
... return result
... return wrapper # 3
>>>def f(a, b):
... return a + b
>>>f = timeit(f) # 4
>>>print(f(1, 2))
3定义一个函数(装饰器)
函数内部定义一个新的函数
返回这个函数
将装饰器应用于f
这段代码定义了一个装饰器,将函数传递给该装饰器后,会返回一个新的函数,功能跟原来的函数一致,且增添了统计时间的功能。再之后的使用中,不需要重新编写统计时间的代码。
如果想给新的函数添加统计时间的功能,只需要将这个装饰器应用于这个函数1
2
3
4
5
6
7>>>def f2():
... print('hello world!')
...
>>>f2 = timeit(f2)
>>>f2()
hello world!
语法糖
语法糖(Syntactic sugar)是指对语言的功能没有影响,但是会方便程序员使用的语法。
根据上面的例子,如果想对一个函数应用decorater,需要这样写:1
2>>>def f(): pass
>>>f = decorater(f)
python针对装饰器提供了一个语法糖1
2>>>@decorater
...def f(): pass
二者的作用是一样的,将装饰器应用到函数f上
并且使用@会有一些好处,使用原有的方式时,将应用装饰器的语句f = decorator(f)放到函数体下面时,如果函数体过长,函数的声明语句与装饰器语句距离很远,有损代码的可读性。
如果对一个函数使用多个装饰器,,如果不用@,有以下两种方式1
2
3
4
5
6# 方式一
f = decorator1(f)
f = decorator2(f)
# 方式二
f = decorator2(decorator1(f))
而使用@,代码的可读性与可维护性都比上述两种方式更高1
2
3@decorator1
@decorator2
def f():
总结
由于python中的函数可以作为函数的参数和返回值,使得python中出现一种特殊的函数——装饰器。可以在不修改函数源码的情况下,增强函数的功能。语法糖@使得对函数使用装饰器变得更简单清晰。行香子·过七里濑
苏轼
一叶舟轻,双桨鸿惊。水天清、影湛波平。
鱼翻藻鉴,鹭点烟汀。过沙溪急,霜溪冷,月溪明。
重重似画,曲曲如屏。算当年、虚老严陵。
君臣一梦,今古空名。但远山长,云山乱,晓山青。