好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

通俗讲解python 装饰器

今天带来通俗讲解python 装饰器教程详解

装饰器其实一直是我的一个"老大难"。这个知识点就放在那,但是拖延症。。。

其实在平常写写脚本的过程中,这个知识点你可能用到不多

但在面试的时候,这可是一个高频问题。

一、什么是装饰器

所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。

这一句话理解起来可能没那么轻松,那先来看一个"傻瓜"函数。

放心,绝对不是"Hello World"!

def hello(): print("你好,装饰器")

肿么样,木骗你吧? 哈哈,这个函数不用运行相信大家都知道输出结果:"你好,装饰器"。

那如果我想让 hello() 函数再实现个其他功能,比如多打印一句话。

那么,可以这样"增强"一下:

def my_decorator(func): def wrapper(): print("这是装饰后具有的新输出") func() return wrapper def hello(): print("你好,装饰器") hello = my_decorator(hello) hello()

运行结果:

这是装饰后具有的新输出 你好,装饰器 [Finished in 0.1s]

很显然,这个"增强"没啥作用,但是可以帮助理解装饰器。

当运行最后的 hello() 函数时,调用过程是这样的:

hello = my_decorator(hello) 中,变量hello指向的是 my_decorator() my_decorator(func) 中传参是 hello ,返回的 wrapper ,因此又会调用到原函数 hello() 于是乎,先打印出了 wrapper() 函数里的,然后才打印出 hello() 函数里的

那上述代码里的 my_decorator() 就是一个装饰器。 它改变了 hello() 的行为,但是并没有去真正的改变 hello() 函数的内部实现。

但是,python一直以"优雅"被人追捧,而上述的代码显然不够优雅。

二、优雅的装饰器

所以,想让上述装饰器变得优雅,可以这样写:

def my_decorator(func): def wrapper(): print("这是装饰后具有的新输出") func() return wrapper @my_decorator def hello(): print("你好,装饰器") hello()

这里的 @my_decorator 就相当于旧代码的 hello = my_decorator(hello) ,@符号称为语法糖。

那如果还有其他函数也需要加上类似的装饰,直接在函数的上方加上 @my_decorator 就可以,大大提高函数 的重复利用与可读性。

def my_decorator(func): def wrapper(): print("这是装饰后具有的新输出") func() return wrapper @my_decorator def hello(): print("你好,装饰器") @my_decorator def hello2(): print("你好,装饰器2") hello2()

输出:

这是装饰后具有的新输出 你好,装饰器2 [Finished in 0.1s]

三、带参数的装饰器

1. 单个参数

上面的只是一个非常简单的装饰器,但是实际场景中,很多函数都是要带有参数的,比如hello(people_name)。

其实也很简单,要什么我们就给什么呗,直接在对应装饰器的 wrapper() 上,加上对应的参数:

def my_decorator(func): def wrapper(people_name): print("这是装饰后具有的新输出") func(people_name) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) hello("张三")

输出:

这是装饰后具有的新输出 你好,张三 [Finished in 0.1s]

2. 多个参数

但是还没完,这样虽然简单,但是随之而来另一个问题:因为并不是所有函数参数都是一样的, 当其他要使用装饰器的函数参数不止这个一个肿么办?比如:

@my_decorator def hello3(speaker, listener): print("{}对{}说你好!".format(speaker, listener))

没关系,在python里, *args 和 **kwargs 表示接受任意数量和类型的参数,所以我们可以这样 写装饰器里的 wrapper() 函数:

def my_decorator(func): def wrapper(*args, **kwargs): print("这是装饰后具有的新输出") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) @my_decorator def hello3(speaker, listener): print("{}对{}说你好!".format(speaker, listener)) hello("老王") print("------------------------") hello3("张三", "李四")

同时运行下 hello("老王") ,和 hello3("张三", "李四") ,看结果:

这是装饰后具有的新输出 你好,老王 ------------------------ 这是装饰后具有的新输出 张三对李四说你好! [Finished in 0.1s]

3. 自定义参数

上面2种,装饰器都是接收外来的参数,其实装饰器还可以接收自己的参数。 比如,我加个参数来控制下装饰器中打印信息的次数:

def count(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): print("这是装饰后具有的新输出") func(*args, **kwargs) return wrapper return my_decorator @count(3) def hello(people_name): print("你好,{}".format(people_name)) hello("老王")

注意,这里 count 装饰函数中的2个 return . 运行下,应该会出现3次:

这是装饰后具有的新输出 你好,老王 这是装饰后具有的新输出 你好,老王 这是装饰后具有的新输出 你好,老王 [Finished in 0.1s]

4. 内置装饰器@functools.wrap

现在多做一步探索,我们来打印下下面例子中的hello()函数的元信息:

def my_decorator(func): def wrapper(*args, **kwargs): print("这是装饰后具有的新输出") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) print(hello.__name__) #看下hello函数的元信息

输出:

wrapper

这说明了,它不再是以前的那个 hello() 函数,而是被 wrapper() 函数取代了。

如果我们需要用到元函数信息,那怎么保留它呢?这时候可以用内置装饰器 @functools.wrap 。

import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print("这是装饰后具有的新输出") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): print("你好,{}".format(people_name)) print(hello.__name__)

 运行下:

hello [Finished in 0.1s]

四、类装饰器

装饰器除了是函数之外,也可以是类。

但是类作为装饰器的话,需要依赖一个函数__call__(),当调用这个类的实例时,函数__call__()就 会被执行。

来改造下之前的例子,把函数装饰器改成类装饰器:

class MyDecorator(): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print("这是装饰后具有的新输出") return self.func(*args, **kwargs) # def my_decorator(func): # def wrapper(): # print("这是装饰后具有的新输出") # func() # return wrapper @MyDecorator def hello(): print("你好,装饰器") hello()

运行:

这是装饰后具有的新输出 你好,装饰器 [Finished in 0.1s]

跟函数装饰器一样,实现一样的功能。

五、装饰器的嵌套

既然装饰器可以增强函数的功能,那如果有多个装饰器,我都想要怎么办? 其实,只要把需要用的装饰器都加上去就好了:

@decorator1 @decorator2 @decorator3 def hello(): ...

但是要注意这里的执行顺序,会从上到下去执行,可以来看下:

def my_decorator(func): def wrapper(): print("这是装饰后具有的新输出") func() return wrapper def my_decorator2(func): def wrapper(): print("这是装饰后具有的新输出2") func() return wrapper def my_decorator3(func): def wrapper(): print("这是装饰后具有的新输出3") func() return wrapper @my_decorator @my_decorator2 @my_decorator3 def hello(): print("你好,装饰器") hello()

运行

这是装饰后具有的新输出 这是装饰后具有的新输出2 这是装饰后具有的新输出3 你好,装饰器 [Finished in 0.1s]

好记性不如烂笔头,写一下理解一下会好很多。

以上就是通俗讲解python 装饰器的详细内容,更多关于python 装饰器的资料请关注自学php网其它相关文章!

以上就是关于通俗讲解python 装饰器全部内容,感谢大家支持自学php网。

查看更多关于通俗讲解python 装饰器的详细内容...

  阅读:39次