python装饰器
概述:
如果彻底弄懂装饰器的问题,建议您耐心读完整篇博客,相信您一定会有收获
装饰器是一个可以调用的对象,其参数是另外一个函数的函数名,函数装饰器可能会处理被装饰的函数,然后将其返回,或者将其替代为另外一个函数或者可调用的对象。
@decorate
def func():
print "func"
上面是函数装饰器最简单的用法,其作用等同于下面的写法
def func():
print "func"
func = decorate(target)
以下问题的答案有助于弄清楚并且掌握装饰器:
- python装饰器的一般语法及装饰器何时执行
- 对于最简单的装饰器来讲,装饰器的形式是函数的嵌套,即内外两层函数
- 外部函数的参数是被装饰函数的函数名,返回值为内部函数的函数名
- 内部函数接受参数为被装饰函数的参数,返回值为被装饰函数的返回值
- 函数装饰器在导入模块时立即执行
# 最典型的函数装饰器形式 >>>def decorater(func): def inner(*args, **kwargs): return func() return inner # 装饰器的使用 >>>@decorater def func(): print("running func") >>>func() running func >>>func <function decotater/<local>.inner at 0x1041543832>
- python如何判断变量的作用域
首先应该知道,函数中的变量分为L、E、G、B,其中E是Enclosing function locals,即嵌套函数内的变量,可以称之为自由变量;B代表Buildin,Python内置模块的名字空间函数名称等,比如dict、len()等;下面重点讨论一下L:local局部变量和G:global全局变量
在下面的三个示例中,变量a就是局部变量L,b则不一定
# 先看一个没有问题的
>>>def func(a):
print(a)
print(b)
>>>func(1)
1
# 看一个有问题的
b = 2
>>>def func(a):
print(a)
print(b)
b =3
>>>func(1)
Traceback (most recent call last):
File "/Users/wangjunjie/Desktop/py_folder/singlepatchdemo.py", line 6, in <module>
func(1)
File "/Users/wangjunjie/Desktop/py_folder/singlepatchdemo.py", line 4, in func
print(b)
UnboundLocalError: local variable 'b' referenced before assignment
# 看一个有问题的
b = 2
>>>def func(a):
global b # 将b声明为全局变量,此时为G
print(a)
print(b)
b =3
>>>func(1)
# 为什么会这样呢?
那是因为python在编译函数时候,由于存在函数内部赋值,将b判断为局部变量,那由于函数内部变量赋值在print后面,所有报错
# 如何解决上面的问题?
b = 2
>>>def func(a):
global b
print(a)
print(b)
b =3
>>>func(1)
1
2
- 闭包是什么?
维基百科是这么写的:在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量(即我们上面的E变量),则可能产生闭包
总结以下,形成闭包的三要素:
~函数的嵌套
~内部函数引用外部E变量
~外部函数返回内部函数 - nonlocal是什么,有什么作用?
其实下面的问题我们上面已经遇到过了,我们在执行count+=1时候,隐式的将count变成了局部变量,此时count没有初始值
#先看一个问题
>>>def average_func():
count = 0
total = 0
def average_inner(num):
count += 1
total += num
return total / count
>>>func = average_func()
>>>func(5)
Trackback (most recent call last)
···
UnboundLocalError: Local cariable "count referenced before addignment
#怎么解决这个问题呢?
>>>def average_func():
count = 0
total = 0
def average_inner(num):
nonlocal count, total # 此时通过nonlocal将变量声明为自由变量
count += 1
total += num
return total / count
功能完善的装饰器
理解以上的内容都是为了能够为了更好的理解装饰器,下面我们首先定义一个功能完整的装饰器,然后再对这个装饰器进行解释
完整装饰器的功能:1.实现函数的功能拓展;2.被装饰的函数可以有参数;3.装饰器可以有参数
#我们定义一个可以接受参数的装饰器,来实现不同形式的输出
import time
def get_run_time(flag):
"""如果flag值为1 print输出的结果为整数;其他则输出浮点数类型
装饰器工厂函数的功能
1. 接收额外参数-->内部代码使用
2. 产生装饰器对象
"""
def get_time(*args, **kwargs):
# 打包参数 args = (99,) kwargs={}
func = args[0]
def inner(*args, **kwargs):
begin = time.time()
# 解包参数 (99) 暂存返回值 在最后返回
ret = func(*args, **kwargs)
end = time.time()
if flag == 1:
print("函数执行花费%d秒" % int(end-begin))
else:
print("函数执行花费%f秒" % (end-begin))
return ret
return inner
return get_time
下面针对上面的装饰器进行说明:
使用functools.wraps完善你的装饰器
仔细分析下面的示例的输出结果,就能理解该函数的用途
叠加装饰器
@d1
@d2
def f():
print("f")
# 等价于
def f():
print("f")
f = d1(d2(f))
安利几个有用的装饰器函数
- python中内置的:property、classmethod、staticmethod
- 标准库中:缓存装饰器 functools.lru_cache、单分派泛函数 functools.singledispatch