详解python装饰器(一)

函数:python中的一等对象

函数也是对象

要想理解装饰器的语法、原理以及运行过程,首先要理解一个概念:在python中,函数是一等对象。

所谓一等对象是指满足一下条件的实体:

  • 在运行时创建
  • 能赋值给变量或数据结构中的元素
  • 能作为参数传给函数
  • 能作为函数的返回结果

在Java或者C++中, 整数、字符、数组都是一等对象,这些在python中也不例外,但是在python里函数也是一等对象,同样满足以上条件,因此常被人简称“一等函数”。

举个例子

def foo():
    print('hello world!')

>>> foo()  # 1
hello world!

>>> type(foo)  # 2
function

>>> foo.__name__  #3
'foo'

以上三部分代码分别表示:

  1. foo是一个函数,可以调用
  2. foo是function类的一个实例
  3. foo具有一个属性,名为__name__,即函数名

用函数来处理函数

既然函数也是一等对象,那么既然函数可以用来处理基本类型,自然也就可以用来处理函数。

举个例子:


>>>  def f():  # 1
...     '''
...     f is a function
...     '''
...     pass  

>>> f.__doc__  # 2
'\n\tf is a function\n\t'

>>> def re_doc(func, doc):  # 3
...     func.__doc__ = doc
...     return func

>>> f = re_doc(f, 'do nothing')  # 4

>>>  f.__doc__  # 5
'do nothing'
  1. 定义一个函数f,以及函数说明__doc__
  2. 函数说明
  3. 定义一个函数,功能是修改一个函数的__doc__属性,参数是一个函数和一个字符串,返回值是一个函数
  4. 调用re_doc
  5. f.__doc__被修改了

用函数来处理函数,就是这么简单。

函数内定义函数

既然可以函数内部定义一个变量,那么同样作为python中的一等对象,函数也可以在函数的内部定义

>>> def outer():
...     a = 0  # 1
...
...     def inner():  # 2
...             print('inner function')
...     inner()  # 3

...     print('outer function')


>>> outer()
inner function
outer function

  1. 在函数内部定义一个变量a
  2. 在函数内部定义一个函数inner
  3. 调用inner

装饰器:用函数修改函数

假如现在有一个需求,需要函数运行结束后打印其运行时间,那么传统的做法又两种,一种是修改函数内部的代码,加入时间统计,二是在调用函数的地方统计。

# 未修改的代码

func1()

func2()

func3()

且不讨论实现这两种方式会影响到代码本身的结构,如果要统计的函数很多,那绝对是一场灾难。


# 修改后的代码

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)的功能,而不需要修改其源代码或定义子类。

写一个装饰器

现在我们使用装饰器来实现上述需求

>>> import time
>>> def timeit(func):  # 1
...     def wrapper(*args, **kwargs):  # 2
...          start_time = time.time()
...          result = func(*args, **kwargs)
...          print('<run %s, cost time:%.4fs>' %(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))
<run f, cost time:0.0000s>
3
  1. 定义一个函数(装饰器)
  2. 函数内部定义一个新的函数
  3. 返回这个函数
  4. 将装饰器应用于f

这段代码定义了一个装饰器,将函数传递给该装饰器后,会返回一个新的函数,功能跟原来的函数一致,且增添了统计时间的功能。再之后的使用中,不需要重新编写统计时间的代码。
如果想给新的函数添加统计时间的功能,只需要将这个装饰器应用于这个函数

>>> def f2():
...     print('hello world!')
...
>>> f2 = timeit(f2)
>>> f2()
hello world!
<run f2, cost time:0.0000s>

语法糖

语法糖(Syntactic sugar)是指对语言的功能没有影响,但是会方便程序员使用的语法。
根据上面的例子,如果想对一个函数应用decorater,需要这样写:

>>> def f(): pass
>>> f = decorater(f)

python针对装饰器提供了一个语法糖

>>> @decorater
... def f(): pass

二者的作用是一样的,将装饰器应用到函数f

并且使用@会有一些好处,使用原有的方式时,将应用装饰器的语句f = decorator(f)放到函数体下面时,如果函数体过长,函数的声明语句与装饰器语句距离很远,有损代码的可读性。

如果对一个函数使用多个装饰器,,如果不用@,有以下两种方式

# 方式一
f = decorator1(f)
f = decorator2(f)

# 方式二
f = decorator2(decorator1(f))

而使用@,代码的可读性与可维护性都比上述两种方式更高

@decorator1
@decorator2
def f():

总结

由于python中的函数可以作为函数的参数和返回值,使得python中出现一种特殊的函数——装饰器。可以在不修改函数源码的情况下,增强函数的功能。语法糖@使得对函数使用装饰器变得更简单清晰。

行香子·过七里濑

苏轼

一叶舟轻,双桨鸿惊。水天清、影湛波平。

鱼翻藻鉴,鹭点烟汀。过沙溪急,霜溪冷,月溪明。

重重似画,曲曲如屏。算当年、虚老严陵。

君臣一梦,今古空名。但远山长,云山乱,晓山青。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值