写代码要遵循开发封闭原则,虽然这个原则是用于面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
封闭:已实现的功能代码块,已经写好的函数不要更改
开放:对扩展开放,
而这正好可以用装饰器来实现。
Python 装饰器用于修改函数。
装饰器本质上是一个函数,它将被装饰的函数作为参数,然后用其返回的函数来替换那个被装饰的函数。
1,最基本的装饰器
>>>defouter(func):
definner():
print("change~"+func())
ret = func()# 1
returnret +"~1"
returninner
>>>deffoo():
return"foo"
>>> foo = outer(foo)#2
change~foo
'foo~1'
# 下面语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约#翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种#语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加#程序的可读性,从而减少程序代码出错的机会。
#只有CPU的微代码不是语法糖,其余代码包括机器语言都是语法糖
>>> @outer
deffoo():
return"foo"
change~foo
'foo~1'
请仔细看这个装饰器示例。首先,定义了一个带单个参数 func 的名为 outer 的函数。然后在 outer 内部定义了一个内嵌函数 inner。inner 函数将打印一行字符 串 ,用来直观的告诉读者这里对要修改的函数进行了修改(change~foo)。并 在 #1 处获取其返回值。在每次 outer 被调用时,func 的值可能都会不同,但不论 func 是什么函数,都将调用它。最后,inner 返回 func() 的返回值加"~ 1"。在 #2 处可以看到,当调用赋值给 foo 的返回函数时,得到的是一行文本输出和返回值 'foo~1',而非期望的调用foo 的返回值 "foo"。
我们可以说等号左边的foo是原foo的装饰版 ,outer将函数foo装修一新了。
2,注册装饰器
_functions= {}
defregister(f):
global_functions
_functions[f.__name__] = f
returnf
@register
deffoo():
return"bar"
在这个例子中,函数被注册并存储在一个字典里,以便后续可以根据函数名字提取函数。
3,被装饰函数带一个固定参数的装饰器
importtime
defdecorator(fun):defwrapper(name):start = time.time()fun(name) runtime = time.time()-start
print(runtime)
returnwrapper
@decorator
defdo_something(name):
foriinrange(1000000):
passprint("play game "+ name)
do_something("san guo sha")
结果如下:
play game san guo sha
0.109375
实现很简单, 就是给wrapper函数加入相同的参数
4,被装饰函数带二个固定参数的装饰器
importtime
defdecorator(fun):
defwrapper(name1,name2):
start = time.time()
fun(name1,name2)
runtime = time.time()-start
print(runtime)
returnwrapper
@decorator
defdo_something(name1,name2):
foriinrange(1000000):
pass
print("play game "+name1+" & "+name2)
do_something("san guo sha","game2")
结果如下:
play game san guo sha & game2
0.109375
5. 目标函数带不固定参数的装饰器
importtime
defdecorator(fun):
defwrapper(*args, **kwargs):
start = time.time()
fun(*args, **kwargs)
runtime = time.time()-start
print(runtime)
returnwrapper
@decorator
defdo_something(name):
foriinrange(1000000):
pass
print("play game "+ name)
@decorator
defdo_something2(user, name):
foriinrange(1000000):
pass
print(user+" play game "+ name)
do_something("san guo sha")
do_something2("wang xiao er","san guo sha")
结果如下:
play game san guo sha
0.109375
wang xiao er play game san guo sha
0.125
6. 让装饰器带参数
importtime
defdecorator(max):
def_decorator(fun):
defwrapper(*args, **kwargs):
start = time.time()
foriinrange(max):
fun(*args, **kwargs)
runtime = time.time()-start
print(runtime)
returnwrapper
return_decorator
@decorator(2)
defdo_something(name):
foriinrange(1000000):
pass
print("play game "+ name)
do_something("san guo sha")
运行结果如下:
play game san guo sha
play game san guo sha
0.25
等同于下面
importtime
defdecorator(max):
def_decorator(fun):
defwrapper(*args, **kwargs):
start = time.time()
foriinrange(max):
fun(*args, **kwargs)
runtime = time.time()-start
print(runtime)
returnwrapper
return_decorator
defdo_something(name):
foriinrange(1000000):
pass
print("play game "+ name)
#@decorator(2)
do_something= decorator(2)(do_something)
do_something("san guo sha")
play game san guo sha
play game san guo sha
0.265625
7. 一个函数可以被多个装饰器装饰
defd1(func):
definner(*args,**kwargs):
# 验证1
returnfunc(*args,**kwargs)
returninner
def(func):
definner(*args,**kwargs):
# 验证1
returnfunc(*args,**kwargs)
returninner
@d1
@d2
deff(arg1,arg2,arg3):
print('f')
f(1,2,3)
运行结果如下:
f
相当于:
defd1(func):
definner(*args,**kwargs):
# 验证1
returnfunc(*args,**kwargs)
returninner
def(func):
definner(*args,**kwargs):
# 验证1
returnfunc(*args,**kwargs)
returninner
deff(arg1,arg2,arg3):
print('f')
f = d1(d2(f))
f(1,2,3)
运行结果如下:
f
另外形式:
@f1(arg)
@f2
deffunc():pass
相当于
deffunc():pass
func = f1(arg)(f2(func))
8. 类也可以装饰:就像装饰函数一样
@f1(arg)
@f2
classFoo:pass
大致相当于
classFoo:pass
Foo = f1(arg)(f2(Foo))
装饰器表达式的计算规则与函数装饰器的计算规则相同。然后将结果绑定到类名。
准确理解—与曾工一块学习
领取专属 10元无门槛券
私享最新 技术干货