首页 > 代码库 > Python装饰器详解
Python装饰器详解
python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法、通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性。
在学习python装饰器之前我们先看看这样一个例子:
一、作用域
# coding:utf-8 msg = ‘hello test1‘ def add(): msg = ‘this is add‘ print msg #当执行add()时将打印‘this is add‘
def add2():
print msg #当执行add2()时将打印‘hello test1‘
上面的例子简单地对python的作用域做了一个说明,例子中申明了全局变量msg,add函数中也申明了一个局部变量msg,当执行add()的 "print msg" 时会先找add里面是否有局部变量msg,如果没找到就去上一级作用域找是否存在该变量,局部变量在函数运行时生成,当函数结束运行时局部变量也随之销毁,接下来我们对上面的例子加深:
二、闭包
# coding:utf-8 def add(): msg = ‘hello add‘ def inner(): print msg #运行add()这里会打印‘hello add‘ return inner
>>> obj = add() >>> obj #可见obj是一个指向inner函数的对象 <function inner at 0x0000000002855438> ... >>> obj() hello add #运行obj()打印msg
看了上面的例子有没有一点疑惑,obj是指向inner函数的对象,当运行obj时就等同于运行inner,但是add函数并没有运行,也就是说msg没有被申明同时inner也没有申明变量msg,它又是如何找到msg这个变量的值呢?
这就是python里的"闭包",Python支持一个叫做函数闭包
的特性,用人话来讲就是,嵌套定义在非全局作用域
里面的函数能够记住它在被定义的时候它所处的封闭命名空间。这能够通过查看函数的obj.func_closure
属性得出结论,这个属性里面包含封闭作用域里面的值(只会包含被捕捉到的值,如果在add
里面还定义了其他的值,封闭作用域里面是不会的)
闭包就是python装饰器的核心原理,接下来我们写一个简单的装饰器例子:
三、简单的装饰器
# coding:utf-8 def check_args_num(func): # 该装饰器用于检查传入的参数数量,检查是否是两个参数 def inner(*args, **kwargs): args_list = list(args) if len(args_list) < 2: for i in range(2 - len(args)): # 如果参数数量小于2则用0填补 args_list.append(0) if len(args_list) > 2: # 如果参数数量大于2则打印错误 print ‘The args number is too many!‘ func(*args_list, **kwargs) return inner @check_args_num def add(x, y): return x + y
执行结果:
>>>print add(1,2) 3 ... >>>print add(100) 100 ... >>>print add(1,2,3) Traceback (most recent call last): File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_exec.py", line 3, in Exec exec exp in global_vars, local_vars File "<input>", line 1, in <module> File "E:/code/my_project/decorator/test1.py", line 14, in inner raise Exception(‘The args number is too many!‘) Exception: The args number is too many! ... >>>add <function inner at 0x0000000002A6C3C8> #可以看到add函数现在指向的是inner
四、多个装饰器
# coding:utf-8 def check_args_int(func): # 该装饰器用于检查传入的参数是否是int型 def ensure_int(*args, **kwargs): from array import array try: array(‘H‘, args) except Exception, e: raise Exception(e) return func(*args, **kwargs) return ensure_int def check_args_num(func): # 该装饰器用于检查传入的参数数量,检查是否是两个参数 def inner(*args, **kwargs): args_list = list(args) if len(args_list) < 2: for i in range(2 - len(args)): # 如果参数数量小于2则用0填补 args_list.append(0) if len(args_list) > 2: # 如果参数数量大于2则打印错误 raise Exception(‘The args number is too many!‘) return func(*args_list, **kwargs) return inner @check_args_num @check_args_int def add(x, y): return x + y
这里多加了一个参数内容检查的装饰器,当多个装饰器时,会自上而下执行,先执行check_args_num再执行check_args_int,执行结果:
>>> print add(1,‘fsaf‘) Traceback (most recent call last): File "D:\PyCharm 5.0.4\helpers\pydev\pydevd_exec.py", line 3, in Exec exec exp in global_vars, local_vars File "<input>", line 1, in <module> File "E:/code/my_project/decorator/test1.py", line 28, in inner return func(*args_list, **kwargs) File "E:/code/my_project/decorator/test1.py", line 10, in ensure_int raise Exception(e) Exception: an integer is required ... >>> add <function inner at 0x0000000002B1C4A8> #这里可以看到add还是指向inner,我们可以这样理解当多个装饰器存在时,当调用add时,其调用入口始终是第一个装饰器,第一个装饰器执行完,再执行下一个,同时也会将参数依次传递下去
五、带参数的装饰器
我们知道定义装饰器的时候传入装饰器的第一个参数是被装饰的函数(eg.例子中的add),有些时候我们也需要给装饰器传递额外的参数,下面例子将给装饰器传递额外参数,如下:
# coding:utf-8 def check_args_int(func): # 该装饰器用于检查传入的参数是否是int型 def ensure_int(*args, **kwargs): from array import array try: array(‘H‘, args) except Exception, e: raise Exception(e) return func(*args, **kwargs) return ensure_int def check_args_num(flag): ‘‘‘ :param func: 被装饰函数 :param flag: 决定是否检查参数数量 ‘‘‘ # 该装饰器用于检查传入的参数数量,检查是否是两个参数 def get_func(func): def inner(*args, **kwargs): if flag == ‘false‘: print ‘Skip check !‘ return func(*args, **kwargs) args_list = list(args) if len(args_list) < 2: for i in range(2 - len(args)): # 如果参数数量小于2则用0填补 args_list.append(0) if len(args_list) > 2: # 如果参数数量大于2则打印错误 raise Exception(‘The args number is too many!‘) return func(*args_list, **kwargs) return inner return get_func @check_args_num(‘false‘) @check_args_int def add(x, y): return x + y
这个例子只有check_args_num和之前的有所不同,不同在于装饰器check_args_num多了一个参数flag,当flag==‘false‘时,跳过参数数量检查,下面是输出结果
>>>print add(1, 2) Skip check ! 3
Python装饰器详解