5个python常用的装饰器!

2023-05-16

大家好呀,我是阿潘。

首先,每个开发人员的目标都是让事情正常进行。慢慢地,我们担心可读性和可扩展性。这是我们第一次开始考虑装饰器的时候。

装饰器是为函数提供额外行为的绝佳方式。 

使用装饰器,你会惊讶地发现可以减少代码重复并提高可读性。

以下是我在几乎每个数据密集型项目中使用的五个最常用的方法。

1.重试装饰器

在数据科学项目和软件开发项目中,有很多我们依赖外部系统的情况。事情并不总是在我们的控制之中。

当意外事件发生时,我们可能希望我们的代码等待一段时间,让外部系统自行纠正并重新运行。

我更喜欢在 python 装饰器中实现这个重试逻辑,这样我就可以注释任何函数来应用重试行为。

这是重试装饰器的代码。

import time
from functools import wraps
def retry(max_tries=3, delay_seconds=1):
    def decorator_retry(func):
        @wraps(func)
        def wrapper_retry(*args, **kwargs):
            tries = 0
            while tries < max_tries:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    tries += 1
                    if tries == max_tries:
                        raise e
                    time.sleep(delay_seconds)
        return wrapper_retry
    return decorator_retry
@retry(max_tries=5, delay_seconds=2)
def call_dummy_api():
    response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
    return response

在上面的代码中,我们尝试获取 API 响应。如果失败,我们将重试相同的任务 5 次。在每次重试之间,我们等待 2 秒。

2.缓存函数结果

我们代码库的某些部分很少改变它们的行为。然而,它可能会占用我们很大一部分计算能力。在这种情况下,我们可以使用装饰器来缓存函数调用。

如果输入相同,该函数将只运行一次。在随后的每次运行中,结果将从缓存中提取。因此,我们不必一直执行昂贵的计算。

def memoize(func):
    cache = {}
    def wrapper(*args):
        if args in cache:
            return cache[args]
        else:
            result = func(*args)
            cache[args] = result
            return result
    return wrapper

装饰器使用字典,存储函数参数,并返回值。当我们执行此功能时,装饰器将检查字典中的先前结果。只有在之前没有存储值时才会调用实际函数。

下面是一个计算斐波那契数列的函数。由于这是一个循环函数,所以调用的同一个函数会执行多次。但是通过缓存,我们可以加快这个过程。

@memoize
def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

以下是使用和不使用缓存时此函数的执行时间。请注意,缓存版本只需要几分之一毫秒即可运行,而非缓存版本几乎需要一分钟。

Function slow_fibonacci took 53.05560088157654 seconds to run.
Function fast_fibonacci took 7.772445678710938e-05 seconds to run.

使用字典来保存以前的执行数据是一种直接的方法。但是,有一种更复杂的方法来存储缓存数据。您可以使用内存数据库,例如 Redis。

3.计时功能

这一点并不奇怪。在处理数据密集型函数时,我们渴望了解运行需要多长时间。

通常的做法是收集两个时间戳,一个在函数的开头,另一个在函数的结尾。然后我们可以计算持续时间并将其与返回值一起打印。

但是一次又一次地为多个函数这样做是一件麻烦事。

相反,我们可以让装饰者来做。我们可以注释任何需要打印持续时间的函数。

这是一个 Python 装饰器示例,它在调用函数时打印函数的运行时间:

import time


def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__} took {end_time - start_time} seconds to run.")
        return result
    return wrapper

您可以使用此装饰器来计时函数的执行:

@timing_decorator
def my_function():
    # some code here
    time.sleep(1)  # simulate some time-consuming operation
    return

调用该函数将打印运行所需的时间。

my_function()


>>> Function my_function took 1.0019128322601318 seconds to run.

4.记录函数调用这个在很大程度上是前一个装饰器的扩展。但它有一些特殊用途。

如果您遵循软件设计原则,您会喜欢单一职责原则。这实质上意味着每个功能将承担其唯一的责任。

当您以这种方式设计代码时,您还希望记录函数的执行信息。这就是日志装饰器派上用场的地方。

下面的例子说明了这一点。

import logging
import functools


logging.basicConfig(level=logging.INFO)


def log_execution(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f"Executing {func.__name__}")
        result = func(*args, **kwargs)
        logging.info(f"Finished executing {func.__name__}")
        return result
    return wrapper


@log_execution
def extract_data(source):
    # extract data from source
    data = ...


    return data


@log_execution
def transform_data(data):
    # transform data
    transformed_data = ...


    return transformed_data


@log_execution
def load_data(data, target):
    # load data into target
    ...


def main():
    # extract data
    data = extract_data(source)


    # transform data
    transformed_data = transform_data(data)


    # load data
    load_data(transformed_data, target)

上面的代码是 ETL 管道的简化版本。我们有三个独立的函数来处理每个提取、转换和加载。我们已经使用我们的 log_execution 装饰器包装了它们中的每一个。

现在,无论何时执行代码,您都会看到类似这样的输出:

INFO:root:Executing extract_data
INFO:root:Finished executing extract_data
INFO:root:Executing transform_data
INFO:root:Finished executing transform_data
INFO:root:Executing load_data
INFO:root:Finished executing load_data

我们还可以在这个装饰器中打印执行时间。但我希望将它们都放在不同的装饰器中。这样,我就可以选择将哪一个(或两者)用于一个函数。

以下是如何在单个函数上使用多个装饰器。

@log_execution
@timing_decorator
def my_function(x, y):
    time.sleep(1)
    return x + y

5.通知装饰器

最后,生产系统中一个非常有用的装饰器是通知装饰器。

再一次,即使重试几次,即使是经过良好测试的代码库也会失败。当发生这种情况时,我们需要通知相关人员以迅速采取行动。

如果您曾经构建过数据管道并希望它能永远正常工作,那么这并不是什么新鲜事。

每当内部函数执行失败时,以下装饰器都会发送一封电子邮件。在您的案例中,它不一定是电子邮件通知。您可以将其配置为发送 Teams/slack 通知。

import smtplib
import traceback
from email.mime.text import MIMEText


def email_on_failure(sender_email, password, recipient_email):
    def decorator(func):
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                # format the error message and traceback
                err_msg = f"Error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}"
                
                # create the email message
                message = MIMEText(err_msg)
                message['Subject'] = f"{func.__name__} failed"
                message['From'] = sender_email
                message['To'] = recipient_email
                
                # send the email
                with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
                    smtp.login(sender_email, password)
                    smtp.sendmail(sender_email, recipient_email, message.as_string())
                    
                # re-raise the exception
                raise
                
        return wrapper
    
    return decorator


@email_on_failure(sender_email='your_email@gmail.com', password='your_password', recipient_email='recipient_email@gmail.com')
def my_function():
    # code that might fail

结论装饰器是将新行为应用于我们的函数的一种非常方便的方法。没有它们,就会有很多代码重复。

在这篇文章中,我讨论了我最常用的装饰器。您可以根据您的特定需求扩展这些。例如,您可以使用 Redis 服务器来存储缓存响应而不是字典。这将使您能够更好地控制数据,例如持久性。或者您可以调整代码以逐步增加重试装饰器中的等待时间。

在我所有的项目中,我都使用了这些装饰器的某些版本。尽管它们的行为略有不同,但这些是我经常使用装饰器的共同目标。

我希望这篇文章对你有所帮助。

本篇文章翻译自:https://towardsdatascience.com/python-decorators-for-data-science-6913f717669a

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

5个python常用的装饰器! 的相关文章

  • 单片机flash不足,keil如下方式进行优化

    单片机的flash不够的时候可以使用keil的优化等级进行优化 xff1b 但是使用Opt进行优化的时候会遇到难以预料的错误 xff0c 所以使用时要对一些变量进行标识 xff0c 比如使用volatile关键字 还有其他方式也能减少内存的
  • msp430看门狗定时器

    看门狗定时器用来防止程序因供电电源 空间电磁干扰或其它原因引起的强烈干扰噪声而跑飞的事故 在很多单片机中都内置了看门狗 xff0c 看门狗本身是一个定时器 xff0c 当定时器溢出时即进行系统复位 xff0c 因此需要在程序中对看门狗定时器
  • UART、SPI和IIC详解与比较(超级实用的调试经验)

    1 UART UART有4个pin xff08 VCC GND RX TX xff09 用的TTL电平 低电平为0 0V xff0c 高电平为1 xff08 3 3V或以上 xff09 UART使用的是异步串行通信 UART是两线 xff0
  • RS232、RS485和CAN协议总结与对比

    一 RS232串口是计算机上一种非常通用的设备通信协议 串口的电气特性 xff1a 1 RS 232串口通信最远距离是50英尺 xff1b 2 RS232可做到双向传输 xff0c 全双工通讯 xff0c 最高传输速率20kbps xff1
  • STM32 GPIOx_CRL/GPIOx_CRH 寄存器

    GPIOx CRL GPIOx CRH xff08 x xff1a A F xff09 寄存器用来对GPIO进行端口设置 xff0c 如 xff1a 设置GPIO为输入模式或输出模式 每个寄存器含有32位 xff0c 每4位用来设置1个GP
  • SHT10 温湿度传感器的程序以及调试总结

    没有其他东西 直接上调试注意事项和代码 xff01 xff01 调试要点 xff1a 1 DATA加10K上拉电阻 2 注意示波器观察各个信号 基础要点 xff1a 1 串行时钟输入 xff08 SCK xff09 单片机模拟时钟 2 串行
  • 晶振原理解析

    目录 01 压电效应 02 晶体振荡器的应用 03 无源晶振和有源晶振的区别 04 STM32外接晶振 下文将进一步讲解晶振的原理 xff0c 以及晶振和STM32的关系 01 压电效应 压电效应 xff1a 某些电介质在沿一定方向上受到外
  • 字符串大小的比较

    字符串大小比较的步骤 xff1a 从左至右一位一位比较 xff0c 如果相同 xff0c 则继续下一位 xff0c 如果不同 xff0c 则谁的ASCII大谁的字符串就大如果比较到其中一者已经结束了 xff0c 还没有分出大小 xff0c
  • STM32延时函数的四种方法

    目录 1 普通延时 2 定时器中断 3 查询定时器 4 汇编指令 单片机编程过程中经常用到延时函数 xff0c 最常用的莫过于微秒级延时delay us 和毫秒级delay ms 本文基于STM32F207介绍4种不同方式实现的延时函数 1
  • STM32的FSMC外设简介

    目录 01 FSMC特点 02 AHB接口 03 外部设备地址映射 04 NOR PSRAM控制器 05 外部存储器接口信号 06 NOR PSRAM控制器异步事务 07 模式1 08 模式A 09 代码说明 01 FSMC特点 Flexi
  • STM32使用DMA接收串口数据

    目录 01 概述 02 DMA接收 03 中断 04 代码 01 概述 在之前的文章里 STM32串口详解 和 STM32 DMA详解 文章中 xff0c 详细讲解了STM32的串口和DMA外设 xff0c 本篇文章将不在细述串口和DMA的
  • 指针与数组

    1 定义 指针 xff1a C语言中某种数据类型的数据存储的内存地址 xff0c 例如 xff1a 指向各种整型的指针或者指向某个结构体的指针 数组 xff1a 若干个相同C语言数据类型的元素在连续内存中储存的一种形态 数组在编译时就已经被
  • 关于软件定时器的一些讨论

    1 简介 这里先介绍下软件定时器和硬件定时器的区别 硬件定时器 xff1a CPU内部自带的定时器模块 xff0c 通过初始化 配置可以实现定时 xff0c 定时时间到以后就会执行相应的定时器中断处理函数 硬件定时器一般都带有其它功能 xf
  • 表驱动法在STM32中的应用

    1 概念 所谓表驱动法 Table Driven Approach 简而言之就是用查表的方法获取数据 此处的 表 通常为数组 xff0c 但可视为数据库的一种体现 根据字典中的部首检字表查找读音未知的汉字就是典型的表驱动法 xff0c 即以
  • 关于共享资源保护的思考

    1 引言 先聊聊分享这篇文章的原因 xff0c 在使用STM32时 xff0c 我发现对于GPIO输出操作 xff0c 可以使用GPIOx ODR寄存器 xff0c 也可以使用GPIOx BSRR寄存器 对应的标准外设库API接口有 voi
  • 预编译#error的使用

    1 引言 说到预编译 xff0c 大家立刻就能想到 define if ifdef和 ifndef等熟悉的预编译命令 其实 include xff0c 我们通常放在源文件用来包含头文件 xff0c 它也是预编译命令 当然这不是这篇文章的重点
  • STM32 IIC详解

    目录 1 IIC定义 2 IIC协议规范 2 1 SDA和SCL信号 2 2 数据有效性 2 3 开始和结束信号 2 4 字节格式 2 5 从机地址和读写位 3 计算IIC的频率 4 PCF8536 4 1 Acknowledge 4 2
  • STM32 SPI详解

    目录 1 SPI简介 2 SPI特点 2 1 SPI控制方式 2 2 SPI传输方式 2 3 SPI数据交换 2 4 SPI传输模式 3 工作机制 3 1 相关缩写 3 2 CPOL极性 3 3 CPHA相位 3 4 极性和相位图示 3 5
  • STM32移植LWIP

    目录 01 IAR工程移植 02 修改Keil工程 在上篇文章 LWIP初体验 修改ST官方demo 中我们已经在自己的开发板上实现了简单的TCPsever和TCPclient功能 验证完了硬件 xff0c 接下来的工作就是优化代码 xff
  • 树莓派4B交叉编译工具链安装

    目录 一 安装配置环境介绍 xff1a 1 宿主机环境 xff1a 2 树莓派系统 xff1a 二 获取交叉编译工具链 xff1a 1 从GitHub下载 不推荐 xff1a 1 xff09 下载必要的软件和工具 xff1a 2 xff09

随机推荐

  • 一种复用模块原理图的设计方法(Port)

    在看一个参考设计时 xff0c 发现一种通过使用port来进行Pin Map 从而让子模块图保持干净 xff0c 以便重复利用的方法 子模块图如下 xff1a 在该图左边 xff0c 通过Port符号 xff0c 将芯片所有的信号管脚 xf
  • SourceTree 设置内置对比视图 不diff大文件

    有时候会往仓库里添加pdf rar等格式的大文件 xff0c 本来diff也看不出个差别来 xff0c 但在sourceTree里面添加时 xff0c 软件会自动去做diff xff0c 如果这类文档很大 xff0c 就会导致soucetr
  • win10 docker desktop运行故障自诊断

    在docker desktop运行出错时 xff0c 程序里有一个诊断工具用于本地诊断 xff0c 使用管理员权限打开powershell xff0c 然后依次执行如下语句 xff1a cd 34 C Program Files Docke
  • STC 8051单片机扩展SRAM介绍、使用以及配置

    总述 STC8051系列单片机中很多具有内部扩展的数据存储器SRAM xff08 单片机内部的RAM一般都是SRAM xff0c 区别于SDRAM xff0c 下面叙述中的RAM xff0c 即表示SRAM xff09 xff0c 所谓的内
  • 光电传感器ST188使用总结

    ST188是我接触的第一款红外光电传感器 xff0c 并在很多场合能够很好地发挥作用 首先说一下 xff0c 光电传感器的种类很多 基本的工作原理都是利用光敏二极管接收到一定的红外光信号来实现检测的 按照光电传感器的入光方式 xff0c 可
  • STC管脚上电如何输出低电平

    最近在做一个项目 xff0c 其中电路板部分功能原理是 xff0c STC MCU直接连接ULN2003 xff0c 再驱动ULN2003控制继电器 本来一切正常的 xff0c 后面在细调的时候发现有一个问题 xff0c 就是在电路板上电瞬
  • C++ STL与文件处理操作总结

    STL 标准库 xff0c 英文为Standard Template Library 广义上讲分为三类 xff0c algorithm xff08 算法 xff09 container xff08 容器 xff09 iterator xff
  • 字节序和位序(大小端)

    Endianness 字节序大家见得比较多 xff0c 网络上论述也比较多 这里简要介绍 xff1a 书写十六进制数据时 xff0c 我们习惯上 MSB 在左 xff0c 而 LSB 在右 LSB least significant byt
  • 使用makefile替换Keil进行编译

    KEIL PATH 61 C Keil ARM ARMCC 61 KEIL PATH BIN40 armcc ARMASM 61 KEIL PATH BIN40 armasm ARMAR 61 KEIL PATH BIN40 armar A
  • [C++][原创]ubuntu上C++发送http请求get和post

    找到一个开源项目 xff1a GitHub elnormous HTTPRequest Single header C 43 43 HTTP request class 使用项目都有介绍 xff0c 很简单 xff0c 这里我在ubuntu
  • 网络通信编程(UDP和TCP协议的实现)

    网络通信 1 网络编程入门1 1网络编程概述1 2网络编程三要素第一要素第二要素第三要素 1 3IP地址 xff08 网络中设备的唯一标识 xff09 常用命令特殊IP地址 1 4InetAddress类的使用 xff08 为了方便对IP地
  • STM32F0 HAL库的串口中断调用顺序

    首先在主函数里执行发送中断或者接收中断函数 xff1a HAL UART Receive IT amp UartHandle uint8 t RxBuf 1 HAL UART Transmit IT amp UartHandle uint8
  • 最全综述 | 图像分割算法

    点击上方 AI算法与图像处理 xff0c 选择加 34 星标 34 或 置顶 重磅干货 xff0c 第一时间送达 图像分割是计算机视觉研究中的一个经典难题 xff0c 已经成为图像理解领域关注的一个热点 xff0c 图像分割是图像分析的第一
  • CNN的Flatten操作 | Pytorch系列(七)

    点击上方 AI算法与图像处理 xff0c 选择加 34 星标 34 或 置顶 重磅干货 xff0c 第一时间送达 文 AI study 欢迎回到这个关于神经网络编程的系列 在这篇文章中 xff0c 我们将可视化一个单一灰度图像的张量flat
  • PyTorch中Linear层的原理 | PyTorch系列(十六)

    点击上方 AI算法与图像处理 xff0c 选择加 34 星标 34 或 置顶 重磅干货 xff0c 第一时间送达 文 AI study 原标题 xff1a PyTorch Callable Neural Networks Deep earn
  • python-opencv报错:QObject::moveToThread: Current thread

    报错 xff1a QObject moveToThread Current thread 0x55ab2a343120 is not the object s thread 0x55ab2a4f8820 Cannot move to tar
  • 谷歌又放大招 Disco Diffusion!AI生成超高质量绘画!

    En点击下方 AI算法与图像处理 xff0c 一起进步 xff01 重磅干货 xff0c 第一时间送达 大家好 xff0c 我是 阿潘 xff5e 我在b站刷到了一个博主分享最新的算法 xff0c 用AI生成高质量的插画 本文主要是分享现在
  • ikun必学!python 画一个简单的只因

    大家好呀 xff0c 我是阿潘 现在有很多虚假的ikun 1 看似维护鸡哥 xff0c 实则想吃鸡哥下的蛋 每次看到这种网络攻击 xff0c 鼻子一酸 xff0c 泪流不止 这个世界太不友善了 xff0c 真的不知道面对那么多无端的谩骂他是
  • CVPR2023论文速递(2023.3.23)!已接入ChatGPT总结!共26篇!

    整理 xff1a AI算法与图像处理 CVPR2023论文和代码整理 xff1a https github com DWCTOD CVPR2023 Papers with Code Demo 欢迎关注公众号 AI算法与图像处理 xff0c
  • 5个python常用的装饰器!

    大家好呀 xff0c 我是阿潘 首先 xff0c 每个开发人员的目标都是让事情正常进行 慢慢地 xff0c 我们担心可读性和可扩展性 这是我们第一次开始考虑装饰器的时候 装饰器是为函数提供额外行为的绝佳方式 使用装饰器 xff0c 你会惊讶