首页 > 编程语言 >13 Python面向对象编程:装饰器

13 Python面向对象编程:装饰器

时间:2024-09-05 09:47:11浏览次数:10  
标签:__ 13 面向对象编程 Python decorator func print 装饰 def

本篇是 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!

代码解释:

  1. CallCounter 类有一个构造函数 __init__,它必须接受一个函数作为参数。
  2. 类实现了 __call__ 方法,这使得其实例可以像函数一样被调用。
  3. __call__ 方法中,每次调用被装饰的函数时,都会增加计数器 count 的值,并打印出函数被调用的次数。
  4. 最后,__call__ 方法调用了原始函数 self.func 并返回结果。

标签:__,13,面向对象编程,Python,decorator,func,print,装饰,def
From: https://www.cnblogs.com/GilbertDu/p/18397772

相关文章

  • 用python编写web 界面可以用哪些库
    背景:很多人熟悉python,但不熟悉前端语言js,为了项目快速落地,也不太想去专门学习React/Angular/Vue这些框架,那么就会问一个问题,能不能用Python直接写出一个简单web界面呢?答案是可以的,而且有多种框架可以用。常见的有下面的几种,可自行搜索学习,选用适合自己的 PlotlyDash,基......
  • python2
    /转义字符,告诉python输入的符号就是符号本身,没有其他的含义join函数输入字符串,需要用str()进行转换功能:join:用于将一个可迭代对象(如列表或元组)中的元素连接成一个字符串。它需要一个字符串作为分隔符。append:用于向列表的末尾添加一个元素。它直接修改原列表。......
  • 【Google Play】高德地图13.20.0.1451最新国际版(如何鉴别是否官方?)
    高德地图,您的全方位出行助手,让您无论走到哪里都能熟悉一切。无论是寻找美食还是探索旅游景点,高德地图都能为您提供详尽的信息。有了高德地图在手,您可以轻松畅游世界各地,享受无忧无虑的旅行体验。提供包括驾车、公交和步行在内的多种出行方式智能导航服务,中文界面让使用更......
  • 【Python学习笔记】 第2章 Python如何运行程序
    Python解释器简介我们在Python的官网下载并安装Python后,它会生成一些组件,包括解释器和一套支持库。Python的代码必须在解释器中运行。程序运行程序员的视角一个Python程序仅是一个包含Python语句的文本文件。例:创建script0.py文件,并写上:print("Hello,world!")print(2**1......
  • 每天五分钟深度学习:广播机制(以python语言为例)
    本文重点因为向量化的计算方式导致效率的提升,所以现在很多时候,我们都是用向量化的计算,但是向量化计算有一个问题让人头痛就是维度的问题,本节课程我们将讲解python中的广播机制,你会发现这个机制的优秀之处。代码实例importnumpyasnpa=np.random.randn(3,4)b=np.random.r......
  • 2024年 Windows Python 下载、安装教程,附详细图文
    大家好,今天为大家带来的是2024年WindowsPython下载、安装教程,附详细图文,适用于Python3所有版本,包括Python3.7,Python3.8,Python3.103.9,Python3.10等版本。希望对大家有所帮助Python目前已支持所有主流操作系统,在Linux,Unix,Mac系统上自带Python环境,一般默认装的是Py......
  • 【python】本地local_setting设置与上传仓库
    1.我们在开发过程中,有的配置数据不希望别人看到,例如:数据库信息2.如是我们可以在本地配置一个local_setting.py文件2.1:在setting中最后,导入local_settingtry:from.local_settingimport*exceptImportError:pass2.2:将自己的配置数据,写入到local_setting......
  • 【Python使用】嘿马头条项目从到完整开发教程第8篇:缓存,多级缓存【附代码文档】
    本教程的知识点为:简介1.内容2.目标产品效果ToutiaoWeb虚拟机使用说明数据库理解ORM作用思考:使用ORM的方式选择数据库SQLAlchemy操作1新增2查询all()数据库分布式ID1方案选择2头条使用雪花算法(代码toutiao-backend/common/utils/snowflake)数据库Red......
  • Python基础知识-8(PyQt GUI开发,输出乱码处理)
    (目录)介绍一个VSCode轻量级RestAPI客户端插件:ThunderClient一、关于shebang明确指定解释器#!/usr/bin/python3在shell中寻找第一个python解释器#!/usr/bin/envpython3二、Python类的私有方法/属性Python不支持私有方法/属性,但可以将类成员方法/属性名定义为......
  • 【python】一篇搞懂多解释器与虚拟环境
    一.同一PC上,python同时支持多个版本的解释器共存1.1:安装不同版本python解释器(参考百度资料)#python安装后,解释器目录介绍C:\python39-python.exe[解释器]-Scripts-pip.exe-pip3.exe-pip3.9.exe-Lib-re.py-random.py内......