Python 装饰器

Python 装饰器

装饰器是什么东西呢?想必大家也多少了解过了一点,它就是实现了我们在不修改原有函数的基础上,给函数或方法增加新的功能,其实装饰器本身也是一个函数,她接收一个函数作为参数并返回一个新的函数,这个新的函数通常包含原函数的调用,并包含一额外新增的功能。

那么在了解装饰器之前呢,你需要了解一个东西叫做闭包,只有了解了它你才能更加了解装饰器。那么可以看我之前写的关于闭包的文章。

链接:https://8888666.top/2024/08/05/python%E9%97%AD%E5%8C%85/

代码解释

使用类定义函数装饰器未带参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyBlogDecorator:
def __init__(self, function):
self.function = function

def __call__(self, *args, **kwargs):
print("The blog name is yunshao")
result_info = self.function(*args, **kwargs)
print("The blog is download, now you can see!")
return result_info


@MyBlogDecorator
def open_blog():
print("welcome to my blog")
return "oh,is very nice blog"

print(open_blog())

代码解释: 上述代码就是简单实现了一个装饰器。

1.首先我定义一个函数,这个函数用来模拟我们打开我的网站的一个对话,整个函数就是打印了一串文字,retrun 了一串文字。

2.然后我定义了一个装饰器MyBlogDecorator,在装饰器中首先在__init__方法中我们定义的function 此时传入的并不是一个变量,而是一个函数,这就是装饰器的灵魂所在。

3.那么我们还定义了魔法函数__call__ 在运行类MyBlogDecorator,会自动调用我们的 __call__ 方法,那么在__call__ 方法中我们设置了可以传入参数设置,也就是假设你的open_blog 函数需要传变量,那么这里也是可以完美支持,只不过我们的案例中没写而已。

4.在里面我们也是首先打印了一串文字,然后此时调用了我们的self.function(*args, **kwargs)这里其实就是将open_blog() 函数进行了调用,然后程序会执行open_blog,然后输出”welcome to my blog”, 此时result_info 进行接收return 返回的值,然后打印“The blog is download, now you can see!”,最后程序输出return的值oh,is very nice blog。

完整的输出信息:

1
2
3
4
The blog name is yunshao
welcome to my blog
The blog is download, now you can see!
oh,is very nice blog

相信大家在此刻也就对装饰器了解更加彻底了!

使用类定义函数装饰器带参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyBlogDecorator:
def __init__(self, name):
self.name = name

def __call__(self, function):
def warp(*args, **kwargs):
print(f"hello {self.name}")
print("The blog name is yunshao")
result_info = function(*args, **kwargs)
print("The blog is download, now you can see!")
return result_info
return warp


@MyBlogDecorator("xiaoming")
def example_test():
print("welcome to my blog")
return "oh,is very nice blog"

print(example_test())

代码解释: 那么如果你看了上述的代码解释,我这里就不会在详细的讲解每一步了,这里我主要来讲不一样的地方。

1.首先我们这里发生变化的地方是我们的装饰器增加了参数,也就是name,我们通过MyBlogDecorator进行了传递name是 xiaoming 的参数,但是要注意的是此时我们接收的name就是在__init__ 方法里,和上述不一样的是我们这里不能接收函数了。

2.我们需要写一个函数的装饰器进行接收,也就是在__call__ 里面进行接收fucntion ,然后通过内部的函数 wrap 进行调用,此时我们需要注意在进入__call__中我们首先执行的是return warp 让warp函数执行,然后我们里面的和我们上述讲的程序执行就是一样的了。

代码输出结果:

1
2
3
4
5
hello xiaoming
The blog name is yunshao
welcome to my blog
The blog is download, now you can see!
oh,is very nice blog

给类添加装饰器

1
2
3
4
5
6
7
8
9
10
def cls_MyBlogDecorator(cls):
print("The blog name is yunshao")
return cls


@cls_MyBlogDecorator
class Open_blog:
print("welcome to my blog")

Open_blog()

代码解释:上述代码定义了一个类Open_blog, 然后类里面打印了一串文字,我们定义了一个装饰器cls_MyBlogDecorator,此时装饰器实现了将Open_blog传入到了cls_MyBlogDecorator中, 也就是cls,然后打印了一串文字,return了 cls 也就调用了Open_blog 这个类 输出了 welcome to my blog

程序输出结果:

1
2
welcome to my blog
The blog name is yunshao

那么,如果我重复调用Open_blog() 这个类会发生什么呢?大部分同学应该会说,那肯定输出上面的两串文字 两次,那么现实真的是吗?

1
2
3
4
5
6
7
8
9
10
11
def cls_MyBlogDecorator(cls):
print("The blog name is yunshao")
return cls


@cls_MyBlogDecorator
class Open_blog:
print("welcome to my blog")

Open_blog()
Open_blog()

程序输出:

1
2
welcome to my blog
The blog name is yunshao

哈哈,大家发现了没有,此时我们调用两次也没有执行输出两次,是因为我们的装饰器已经装饰完到内存了,你虽然调用了两次,但是每次调用的还是那个装饰器装饰到内存的那个,所以会只是输出一次,那么怎么让他输出两次呢? 可以这样来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def cls_MyBlogDecorator(cls):
print("The blog name is yunshao")
def inner():
print("the blog is download")
result_object = cls()
print("wow the blog is very nice!")
return result_object
return inner


@cls_MyBlogDecorator
class Open_blog:
print("welcome to my blog")

open1 = Open_blog()
print("===================")
open2 = Open_blog()

代码解释: 此时我们进行了改进在之前的函数中新增了inner函数,此时逻辑变为当我们每次调用的Open_blog 这个类的时候实际我们在调用inner这个函数,函数里面呢将类进行了实例化,此时我们return 底部的也变为了inner,也就是每次调用都会执行inner()这个函数。

程序输出:

1
2
3
4
5
6
7
welcome to my blog
The blog name is yunshao
the blog is download
wow the blog is very nice!
===================
the blog is download
wow the blog is very nice!

这样你就会发现我们每调用一次都会调用一次inner里面的!

总结

python 中的装饰器是一种了非常灵活的方式来增强函数的功能,而不需要修改函数本身的代码。它们是函数式编程在 Python 中的一种优雅体现,能够极大地提高代码的可读性和可维护性。在众多的设计模式中也用的非常多,例如我们的日志记录、性能测试、事务处理、权限校验等,以及我们在使用Pytest 框架的时候我们会用到很多框架提供给我们的装饰器,非常强大且使用方便!


Python 装饰器
https://dreamshao.github.io/2024/08/07/python装饰器/
作者
Yun Shao
发布于
2024年8月7日
许可协议