在python中,函数也是对象,我们可以把函数作为另外一个函数的返回值(也就是闭包),也可以在函数里面嵌套函数。如下:
def func_closure():
print("func_closure")
def get_message(message):
print("got a message:{}".format(message))
return get_message
send_message = func_closure()
send_message("hello world")
#输出
func_closure
got a message:hello world
func_closure()返回了get_message函数对象,并把它赋值给send_message变量,这样,send_message就相当于get_message函数了,可以像get_message()那样使用。
装饰器
装饰器将额外增加的功能,封装在自己的装饰器函数或类中;如果你想要调用它,只需要在原函数的顶部,加上 @decorator 即可。显然,这样做可以让你的代码得到高度的抽象、分离与简化。
看一个简单的装饰器
def my_decorator(func):
def wrapper():
print('wrapper of decorator')
func()
return wrapper
def greet():
print('hello world')
greet = my_decorator(greet)
greet()
#输出
wrapper of decorator
hello world
上述中,函数 my_decorator() 就是一个装饰器,它把真正需要执行的函数 greet() 用wrapper包裹在其中,并且改变了它的行为,但是原函数 greet() 不变。还有更简洁的表述方法,即使用@decorator:
def my_decorator(func):
def wrapper(message):
print("wrapper of decorator")
func(message)
return wrapper
@my_decorator
def greet(message):
print(message)
greet("hello world")
如上,如果想要在函数前后增加额外的功能,将额外增加的功能放在修饰器中,在原函数上顶部加上@your_decorator即可。
假如我们之前是可以随便评论
#原来的提交评论
def commit_comment(text):
print("comment is:{}".format(text))
#输出
#comment is:柯南更新了!
现在需要在评论前判断是否已登录,使用装饰器
def auth(func):
def wrapper(text):
print("Check if the user is logged in")
func(text)
return wrapper
#原来的提交评论
@auth
def commit_comment(text):
print("comment is:{}".format(text))
commit_comment("柯南更新了!")
把检查用户登录写成一个装饰器auth,在现在需要检查用户登录的函数的顶部加上@auth即可。完全无需改动原有的代码逻辑,既简洁又方便。
装饰器的作用与意义,在于其可以通过自定义的函数或类,在不改变原函数的基础上,改变原函数的一些功能。
装饰器wrapper的参数
上面我们已经有一个不传参数的和一个传参数的装饰器。如果一个装饰器要用在几个不同的且参数个数也不同的函数上呢,参数该怎么写呢?
通常情况下,我们会把*args和**kwargs,作为装饰器内部函数 wrapper() 的参数。*args和**kwargs,表示接受任意数量和类型的参数,因此装饰器就可以写成下面的形式:
import functools
def my_decorator(func):
@functools.wraps(func) #使方法名保持原有的方法名
def wrapper(*args,**kwargs):
print("wrapper of decorator")
func(*args,**kwargs)
return wrapper
注意上面的@functools.wraps(func),它能够使方法名保持原有的方法名,如果不加这一行的话,那方法的方法名就变成了wrapper
类装饰器
不仅函数可以做成装饰器,类也可以写成装饰器。类装饰器主要依赖于函数__call__(),每当你调用一个类的实例时,函数__call__()就会被执行一次。比如说你想统计某一个函数被调用了多少次。
class Count:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print('{} num of calls is: {}'.format(id(self),self.num_calls))
return self.func(*args, **kwargs)
@Count
def example(message):
print("message is:{}".format(message))
@Count
def example2(number):
print("number is:{}".format(number))
example("hello world")
example("welcome to shenzhen")
print('-------- 分割线------')
example2(1)
example2(2)
#输出
140414128260528 num of calls is: 1
message is:hello world
140414128260528 num of calls is: 2
message is:welcome to shenzhen
-------- 分割线------
140414128262688 num of calls is: 1
number is:1
140414128262688 num of calls is: 2
number is:2
从上面的例子中可以看到,类装饰器对于同一个函数,只会实例化一次。如果你输出type(example),你会发现,它的类型是Count。在函数定义的时候,它就初始化了成了Count的实例,后面每一次调用其实是Count实例的__call__函数的调用。
装饰器的嵌套
装饰器还可以嵌套,一个函数可以有多个装饰器装饰
@decorator1
@decorator2
def func(): pass
执行顺序是:decorator1->decorator2->func()
Classes can also be decorated: just like when decorating functions
官方文档上说,类也可以被装饰器装饰,如下:
@f1
class Foo: pass
但目前暂时还没想到类装饰器怎么用。
网友评论