欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > python >内容正文

python

一篇文章带你了解python装饰器

发布时间:2025/3/20 python 24 豆豆
生活随笔 收集整理的这篇文章主要介绍了 一篇文章带你了解python装饰器 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

一、什么是装饰器

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

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

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

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

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

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

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

def my_decorator(func):def wrapper():print("这是装饰后具有的新输出")func()return wrapperdef 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一直以"优雅"被人追捧,而上述的代码显然不够优雅。

    二、优雅的装饰器

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

    ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' def my_decorator(func):def wrapper():print("这是装饰后具有的新输出")func()return wrapper@my_decorator def hello():print("你好,装饰器")hello()

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

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

    ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' 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]

    三、带参数的装饰器

  • 单个参数
  • 上面的只是一个非常简单的装饰器,但是实际场景中,很多函数都是要带有参数的,比如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]
  • 多个参数
  • 但是还没完,这样虽然简单,但是随之而来另一个问题:因为并不是所有函数参数都是一样的,
    当其他要使用装饰器的函数参数不止这个一个肿么办?比如:

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

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

    ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' 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]
  • 自定义参数
  • 上面2种,装饰器都是接收外来的参数,其实装饰器还可以接收自己的参数。
    比如,我加个参数来控制下装饰器中打印信息的次数:

    ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' def count(num):def my_decorator(func):def wrapper(*args, **kwargs):for i in range(num):print("这是装饰后具有的新输出")func(*args, **kwargs)return wrapperreturn my_decorator@count(3) def hello(people_name):print("你好,{}".format(people_name))hello("老王")

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

    这是装饰后具有的新输出 你好,老王 这是装饰后具有的新输出 你好,老王 这是装饰后具有的新输出 你好,老王 [Finished in 0.1s]
  • 内置装饰器@functools.wrap
  • 现在多做一步探索,我们来打印下下面例子中的hello()函数的元信息:

    ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' 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 functoolsdef 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__()就
    会被执行。

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

    ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' class MyDecorator():def __init__(self, func):self.func = funcdef __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():...

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

    ''' 遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939 寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书! ''' def my_decorator(func):def wrapper():print("这是装饰后具有的新输出")func()return wrapperdef my_decorator2(func):def wrapper():print("这是装饰后具有的新输出2")func()return wrapperdef 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装饰器的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。