阅读本文大约需要 10 分钟。
在 Python 开发中,我们经常会看到使用装饰器的场景,例如日志记录、权限校验、本地缓存等等。
使用这些装饰器,给我们的开发带来了极大的便利,那么一个装饰器是如何实现的呢?
这篇文章我们就来分析一下,Python 装饰器的使用及原理。
一切皆对象
在介绍装饰器前,我们需要理解一个概念:在 Python 开发中,一切皆对象。
什么意思呢?
就是我们在开发中,无论是定义的变量(数字、字符串、元组、列表、字典)、还是方法、类、实例、模块,这些都可以称作对象。
怎么理解呢?在 Python 中,所有的对象都会有属性和方法,也就是说可以通过「.」去获取它的属性或调用它的方法,例如像下面这样:
# coding: utf8
i = 10 # int对象
print id(i), type(i)
# 140703267064136, <type 'int'>
s = 'hello' # str对象
print id(s), type(s), s.index('o')
# 4308437920, <type 'str'>, 4
d = {
'k': 10} # dict对象
print id(d), type(d), d.get('k')
# 4308446016, <type 'dict'>, 10
def hello(): # function对象
print 'Hello World'
print id(hello), type(hello), hello.func_name, hello()
# 4308430192, <type 'function'>, hello, Hello World
hello2 = hello # 传递对象
print id(hello2), type(hello2), hello2.func_name, hello2()
# 4308430192, <type 'function'>, hello, Hello World
# 构建一个类
class Person(object):
def __init__(self, name):
self.name = name
def say(self):
return 'I am %s' % self.name
print id(Person), type(Person), Person.say
# 140703269140528, <type 'type'>, <unbound method Person.say>
person = Person('tom') # 实例化一个对象
print id(person), type(person),
# 4389020560, <class '__main__.Person'>
print person.name, person.say, person.say()
# tom, <bound method Person.say of <__main__.Person object at 0x1059b2390>>, I am tom
我们可以看到,常见的这些类型:int
、str
、dict
、function
,甚至 class
、instance
都可以调用 id
和 type
获得对象的唯一标识和类型。
例如方法的类型是 function
,类的类型是 type
,并且这些对象都是可传递的。
对象可传递会带来什么好处呢?
这么做的好处就是,我们可以实现一个「闭包」,而「闭包」就是实现一个装饰器的基础。
闭包
假设我们现在想统计一个方法的执行时间,通常实现的逻辑如下:
# coding: utf8
import time
def hello():
start = time.time() # 开始时间
time.sleep(1) # 模拟执行耗时
print 'hello'
end = time.time() # 结束时间
print 'duration time: %ds' % int(end - start) # 计算耗时
hello()
# Output:
# hello
# duration time: 1s
统计一个方法执行时间的逻辑很简单,只需要在调用这个方法的前后,增加时间的记录就可以了。
但是,统计这一个方法的执行时间这么写一次还好,如果我们想统计任意一个方法的执行时间&