13 Python面向对象编程:装饰器
本篇是 Python 系列教程第 13 篇,更多内容敬请访问我的 Python 合集
Python 装饰器是一种强大的工具,用于修改或增强函数或方法的行为,而无需更改其源代码。装饰器本质上是一个接收函数作为参数的函数,并返回一个新的函数。装饰器的用途包括日志记录、性能测试、事务处理、缓存、权限校验等
1 基本语法
装饰器的基本语法是在函数定义之前使用@符号,紧跟着装饰器的名字。例如:
# 定义一个装饰器,参数为被装饰的方法
def my_decorator(func):
def wrapper():
print("方法运行前")
func()
print("方法运行后")
return wrapper
# 用“@”使用装饰器
@my_decorator
def say_hello():
print("Hello!")
say_hello()
这段代码会输出:
方法运行前
Hello!
方法运行后
2 参数传递
如果被装饰的函数需要参数,装饰器也需要相应地处理这些参数:
def my_decorator(func):
def wrapper(name):
print("方法运行前")
func(name)
print("方法运行后")
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
输出:
方法运行前
Hello, Alice!
方法运行后
参数可以用可变参数,比较灵活,如下:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("方法运行前")
func(*args, **kwargs)
print("方法运行后")
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
3 使用多个装饰器
你可以为同一个函数使用多个装饰器:
def decorator_A(func):
print("enter A")
def wrapper():
print("Before A")
func()
print("After A")
print("out A")
return wrapper
def decorator_B(func):
print("enter B")
def wrapper():
print("Before B")
func()
print("After B")
print("out B")
return wrapper
@decorator_A
@decorator_B
def my_function():
print("Inside my_function")
# 执行被装饰器装饰的函数
my_function()
输出:
enter B
out B
enter A
out A
Before A
Before B
Inside my_function
After B
After A
注意打印结果的顺序。
为了方便表达我们先把靠近被修饰方法的装饰器叫内层装饰器,如示例中的@decorator_B,不靠近的叫外层装饰器,如示例中的@decorator_A。
在闭包wrapper外面的代码是内层装饰器先执行,在闭包wrapper内部的代码执行顺序复杂一些:①外层装饰器先执行func() 前面的代码->②内层装饰器执行func() 前面的代码->③执行func() ->④内层装饰器执行func() 后面的代码->⑤外层装饰器执行func() 后面的代码。
4 给装饰器传参
装饰器本身可以接受参数,可以根据传入的不同参数来改变装饰器的行为。
前面的例子都是没有参数的装饰器,如果我们想要给装饰器传参该怎么办呢?于是我们就思考一下,什么东东可以接收参数呢,答案是函数。bingo!Python也是这样设计的,我们只需要在装饰器外面包裹一层函数,就可以把参数传递给函数进而传递给装饰器了。
可以这样定义装饰器:
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
输出:
Hello, Alice!
Hello, Alice!
Hello, Alice!
这样就定义了一个根据传入装饰器的数值执行指定次数函数的装饰器。
5 类作为装饰器
5.1 __call__方法
装饰器不仅仅可以是方法,也可以是类。这就不得不介绍一个特殊的方法__call__。
Python的类只要实现了__call__ 这个特殊方法,类的实例对象就可以像函数一样被调用,因为当尝试把对象写成方法调用的写法时(名称+()),Python 解释器会查找该对象的 __call__ 方法并调用它。
下面来看一个简单的例子,演示__call__的使用:
class Counter:
def __init__(self):
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"方法被调用了 {self.count} 次")
counter = Counter()
# 模拟调用
counter()
counter()
counter()
打印:
方法被调用了 1 次
方法被调用了 2 次
方法被调用了 3 次
5.2 类作为装饰器
类作为装饰器的一个主要优势是可以方便地维护状态,因为类可以有实例变量。
理解了__call__之后,我们可以想到类作为装饰器的原理是在类里实现了__call__方法,使得装饰器的代码可以被执行。
下面我们定义一个记录函数调用次数的装饰器:
class CallCounter:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} 被调用了 {self.count} 次")
return self.func(*args, **kwargs)
@CallCounter
def say_hello(name):
print(f"Hello, {name}!")
# 调用被装饰的函数
say_hello("Alice")
say_hello("Bob")
say_hello("Charlie")
# 输出
# say_hello 被调用了 1 次
# Hello, Alice!
# say_hello 被调用了 2 次
# Hello, Bob!
# say_hello 被调用了 3 次
# Hello, Charlie!
代码解释:
CallCounter类有一个构造函数__init__,它必须接受一个函数作为参数。- 类实现了
__call__方法,这使得其实例可以像函数一样被调用。 - 在
__call__方法中,每次调用被装饰的函数时,都会增加计数器count的值,并打印出函数被调用的次数。 - 最后,
__call__方法调用了原始函数self.func并返回结果。
13 Python面向对象编程:装饰器的更多相关文章
- Python 元编程 - 装饰器
Python 中提供了一个叫装饰器的特性,用于在不改变原始对象的情况下,增加新功能或行为. 这也属于 Python "元编程" 的一部分,在编译时一个对象去试图修改另一个对象的信息 ...
- Python核心编程 | 装饰器
装饰器是程序开发的基础知识,用好装饰器,在程序开发中能够提高效率 它可以在不需要修改每个函数内部代码的情况下,为多个函数添加附加功能,如权限验证,log日志等 涉及点: 1.先梳理一下 ...
- Python面向对象-@property装饰器
python中,我们可以直接添加和修改属性的值: >>> class Student(object): ... pass ... >>> s = Student() ...
- python函数式编程-装饰器
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator). 由于函数也是一个对象,而且函数对象可以赋值给变量,所以通过变量也能调用该函数. >>> def now() ...
- 面向切面编程AOP——加锁、cache、logging、trace、同步等这些较通用的操作,如果都写一个类,则每个用到这些功能的类使用多继承非常难看,AOP就是解决这个问题的,python AOP就是装饰器
面向切面编程(AOP)是一种编程思想,与OOP并不矛盾,只是它们的关注点相同.面向对象的目的在于抽象和管理,而面向切面的目的在于解耦和复用. 举两个大家都接触过的AOP的例子: 1)java中myba ...
- python面向对象编程进阶
python面向对象编程进阶 一.isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 1 ...
- python 面向对象编程(一)
一.如何定义一个类 在进行python面向对象编程之前,先来了解几个术语:类,类对象,实例对象,属性,函数和方法. 类是对现实世界中一些事物的封装,定义一个类可以采用下面的方式来定义: class c ...
- Python面向对象编程指南
Python面向对象编程指南(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1SbD4gum4yGcUruH9icTPCQ 提取码:fzk5 复制这段内容后打开百度网 ...
- python面向对象编程(OOP)
python作为一种解释性语言,其主要的编程方式就是面向对象,而且python的框架django也是主要面向对象的编程. 类(class)和对象(object) 类(class)是用来描述具有相同属性 ...
- Python入门篇-装饰器
Python入门篇-装饰器 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.装饰器概述 装饰器(无参) 它是一个函数 函数作为它的形参 返回值也是一个函数 可以使用@functi ...
随机推荐
- SpringBoot集成MQTT
MQTT介绍 MQTT 是基于 Publish/Subscribe(发布/订阅) 模式的物联网通信协议,凭借简单易实现.支持 QoS.报文小等特点. 其具有协议简洁.⼩巧.可扩展性强.省流量.省电等优 ...
- 树莓派4B-高精度驱动步进电机
树莓派4B-高精度驱动步进电机 项目介绍 利用4B树莓派控制步进电机转动,精度可达:0.0144度 (即360度/25000) 适用于非常精密的角度转动. 舵机的精度为1度,无法实现超高精度控制. 硬 ...
- JVM学习笔记-如何在IDEA打印JVM的GC日志信息
若要在Idea上打印JVM相应GC日志,其实只需在Run/Debug Configurations上进行设置即可. 拿<深入Java虚拟机>书中的3-7代码例子来演示,如 1 public ...
- 如何巧妙使用some函数来优化性能
什么时候会用的array.some? 需要从数组里快速找到符合条件的某一项 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测. 如果没有满足条件的元素,则返回false ...
- 让你的vscode搭载ChatGPT获得来自 AI 的编程指导
一直以来,VS Code 都是开发者心目中的生产力神器,它免费.开源且跨平台,被称为最好用的 IDE. 把 VS Code 和 ChatGPT 结合使用,用户将获得来自 AI 的编程指导,包括代码解释 ...
- Nginx 可视化配置神器NginxConfig
Nginx 是前后端开发工程师必须掌握的神器.该神器有很多使用场景:比如反向代理.负载均衡.动静分离.跨域等等. 把 Nginx 下载下来打开 conf 文件夹的 nginx.conf 文件,Ngin ...
- 开启GitLab的邮件通知功能以及一些外观配置
前言 维护GitLab的同事离职了 刚好又有新实习生需要申请账号 只能我来出手了 其实之前安装了 GitLab 之后一直还是用得比较粗糙的 属于是勉强能用的水平,有些配置都还没改好 这次把邮件功能.域 ...
- [oeasy]python0070_ 字体样式_下划线_中划线_闪动效果_反相_取消效果
字体样式 回忆上次内容 m 可以改变字体样式 0-10 之间设置的都是字体效果 0 复原 1 变亮 2 变暗 从3到10 又是什么效果 呢?? 真的可以blink闪烁吗? 3m 3m 实现斜体字的效果 ...
- 面试题-python 什么是闭包(closure)?
前言 前面学了装饰器,那么闭包和装饰器有什么区别呢?闭包传递的是变量,而装饰器传递的是函数对象,只是传的参数内容不一样,闭包的概念包含了装饰器,可以说装饰器是闭包的一种,它只是传递函数对象的闭包. 先 ...
- centos7 最小化安装yum不能安装软件解决方案
慕课网神思者老师课常资料带的布署工具中,自带的liunx 系统centos7 yum发现不能安装软件,比如docker 解决方案 首先我们安装好虚拟机启动系统centos7 尝试安装任何软件都会报 ...