Python异常处理:基础到进阶的实用指南

Python异常处理进阶:从基础到企业级实践 本文深入探讨Python异常处理的高级技巧和企业级实践。首先解析Python异常类层次结构,对比不同处理场景的性能差异。然后介绍上下文管理器、异常链保留等进阶方法,展示利用装饰器实现自动重试机制。在自定义异常体系部分,详细说明如何创建业务异常类,设计清晰的异常层次结构,并演示如何将异常信息与业务逻辑紧密结合。文章通过大量代码示例和图表,全面呈现了构建健壮Python应用所需的异常处理策略,包括性能优化、资源管理和错误恢复等关键环节。 阅读详情

前言

大家好,我是海鸽。异常处理在工程文件中必不可少,今天就带大家彻底搞定python的异常处理。

什么是异常

在Python中,异常是指在程序执行过程中出现的错误或异常情况。

当Python解释器无法执行代码时,它会引发异常,这可能是由于语法错误、运行时错误或逻辑错误等原因引起的。

我们经常会碰到诸如以下的异常:

SyntaxError异常

Python语言拥有自己的语法格式和规则。如果我们未能遵守这些规则,就会导致异常的出现。

In [52]: pint("Hello, World!")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [52], in <cell line: 1>()
----> 1 pint("Hello, World!")

NameError: name 'pint' is not defined

正确语法为print("Hello, World!")

NameError异常

NameError异常通常在代码中引用了未声明的变量或者不存在的函数或方法时触发。

In [53]: print(my_variable)  # 使用了未定义的变量
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [53], in <cell line: 1>()
----> 1 print(my_variable)

NameError: name 'my_variable' is not defined

KeyError异常

KeyError异常通常在尝试访问字典中不存在的键时触发。

In [54]: my_dict = {'a': 1, 'b': 2}
    ...: print(my_dict['c'])  # 尝试访问字典中不存在的键
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Input In [54], in <cell line: 2>()
      1 my_dict = {'a': 1, 'b': 2}
----> 2 print(my_dict['c'])

KeyError: 'c'

FileNotFoundError(文件未找到错误)

FileNotFoundError异常通常在尝试打开或操作一个并不存在的文件时触发。

In [55]: file = open('non_existent_file.txt', 'r')
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Input In [55], in <cell line: 1>()
----> 1 file = open('non_existent_file.txt', 'r')

FileNotFoundError: [Errno 2] No such file or directory: 'non_existent_file.txt'

异常可以分为两种类型:

  1. 语法错误(Syntax Errors):这类错误也称为解析错误,是由于代码不符合Python语法规则而引起的。比如,拼写错误、缺少括号、不正确的缩进等。

  2. 运行时错误(Runtime Errors):这类错误在程序运行时发生,通常是由于程序的逻辑错误或外部环境引起的。常见的运行时错误包括除以零(ZeroDivisionError)、访问不存在的索引(IndexError)、使用未定义变量(NameError)等。

异常处理机制允许我们捕获并处理这些异常,以避免程序崩溃并提供错误处理和恢复的机会。

在Python中,我们可以使用try-except语句来捕获并处理异常。try语句块用于执行可能会引发异常的代码,而except语句块用于处理异常情况。例如:

try:
    # 可能会引发异常的代码
    result = 10 / 0
except ZeroDivisionError:
    # 处理除以零的异常
    print("除数不能为零!")

除了使用try-except语句来处理异常,还可以使用其他相关的结构和关键字,如try-except-else、try-except-finally等,来更灵活地处理异常情况。

异常的分类2

在Python中,异常可以进一步分为内置异常(Built-in Exceptions)和自定义异常(Custom Exceptions)。

  1. 内置异常(Built-in Exceptions):Python提供了一系列内置的异常类来表示不同类型的错误或异常情况。这些异常类都是内置在Python解释器中的,开发者可以直接使用它们来处理程序执行过程中可能出现的各种错误。常见的内置异常包括:
-- SystemExit  # 解释器请求退出
-- KeyboardInterrupt  # 用户中断执行(通常是输入^C)
-- GeneratorExit  # 生成器(generator)发生异常来通知退出
     -- StopIteration  # 迭代器没有更多的值
     -- StopAsyncIteration  # 必须通过异步迭代器对象的__anext__()方法引发以停止迭代
     -- ArithmeticError  # 各种算术错误引发的内置异常的基类
     |    -- FloatingPointError  # 浮点计算错误
     |    -- OverflowError  # 数值运算结果太大无法表示
     |    -- ZeroDivisionError  # 除(或取模)零 (所有数据类型)
     -- AssertionError  # 当assert语句失败时引发
     -- AttributeError  # 属性引用或赋值失败
     -- BufferError  # 无法执行与缓冲区相关的操作时引发
     -- EOFError  # 当input()函数在没有读取任何数据的情况下达到文件结束条件(EOF)时引发
     -- ImportError  # 导入模块/对象失败
     |    -- ModuleNotFoundError  # 无法找到模块或在在sys.modules中找到None
     -- LookupError  # 映射或序列上使用的键或索引无效时引发的异常的基类
     |    -- IndexError  # 序列中没有此索引(index)
     |    -- KeyError  # 映射中没有这个键
     -- MemoryError  # 内存溢出错误(对于Python 解释器不是致命的)
     -- NameError  # 未声明/初始化对象 (没有属性)
     |    -- UnboundLocalError  # 访问未初始化的本地变量
     -- OSError  # 操作系统错误,EnvironmentError,IOError,WindowsError,socket.error,select.error和mmap.error已合并到OSError中,构造函数可能返回子类
     |    -- BlockingIOError  # 操作将阻塞对象(e.g. socket)设置为非阻塞操作
     |    -- ChildProcessError  # 在子进程上的操作失败
     |    -- ConnectionError  # 与连接相关的异常的基类
     |    |    -- BrokenPipeError  # 另一端关闭时尝试写入管道或试图在已关闭写入的套接字上写入
     |    |    -- ConnectionAbortedError  # 连接尝试被对等方中止
     |    |    -- ConnectionRefusedError  # 连接尝试被对等方拒绝
     |    |    -- ConnectionResetError    # 连接由对等方重置
     |    -- FileExistsError  # 创建已存在的文件或目录
     |    -- FileNotFoundError  # 请求不存在的文件或目录
     |    -- InterruptedError  # 系统调用被输入信号中断
     |    -- IsADirectoryError  # 在目录上请求文件操作(例如 os.remove())
     |    -- NotADirectoryError  # 在不是目录的事物上请求目录操作(例如 os.listdir())
     |    -- PermissionError  # 尝试在没有足够访问权限的情况下运行操作
     |    -- ProcessLookupError  # 给定进程不存在
     |    -- TimeoutError  # 系统函数在系统级别超时
     -- ReferenceError  # weakref.proxy()函数创建的弱引用试图访问已经垃圾回收了的对象
     -- RuntimeError  # 在检测到不属于任何其他类别的错误时触发
     |    -- NotImplementedError  # 在用户定义的基类中,抽象方法要求派生类重写该方法或者正在开发的类指示仍然需要添加实际实现
     |    -- RecursionError  # 解释器检测到超出最大递归深度
     -- SyntaxError  # Python 语法错误
     |    -- IndentationError  # 缩进错误
     |         -- TabError  # Tab和空格混用
     -- SystemError  # 解释器发现内部错误
     -- TypeError  # 操作或函数应用于不适当类型的对象
     -- ValueError  # 操作或函数接收到具有正确类型但值不合适的参数
     |    -- UnicodeError  # 发生与Unicode相关的编码或解码错误
     |         -- UnicodeDecodeError  # Unicode解码错误
     |         -- UnicodeEncodeError  # Unicode编码错误
     |         -- UnicodeTranslateError  # Unicode转码错误
     -- Warning  # 警告的基类
          -- DeprecationWarning  # 有关已弃用功能的警告的基类
          -- PendingDeprecationWarning  # 有关不推荐使用功能的警告的基类
          -- RuntimeWarning  # 有关可疑的运行时行为的警告的基类
          -- SyntaxWarning  # 关于可疑语法警告的基类
          -- UserWarning  # 用户代码生成警告的基类
          -- FutureWarning  # 有关已弃用功能的警告的基类
          -- ImportWarning  # 关于模块导入时可能出错的警告的基类
          -- UnicodeWarning  # 与Unicode相关的警告的基类
          -- BytesWarning  # 与bytes和bytearray相关的警告的基类
          -- ResourceWarning  # 与资源使用相关的警告的基类。被默认警告过滤器忽略。
异常捕获与处理

完整的内置异常列表可以在Python官方文档中找到。
https://docs.python.org/3/library/exceptions.html#bltin-exceptions

  1. 自定义异常(Custom Exceptions):除了使用Python提供的内置异常类外,开发者还可以根据自己的需求定义自己的异常类。通过定义自定义异常类,开发者可以更好地组织和管理程序中可能出现的特定类型的错误。自定义异常类通常继承自Python内置的Exception类或其子类。例如:
class CustomException(Exception):
    def __init__(self, message):
        self.message = message

# 在某个函数中引发自定义异常
def example_function(x):
    if x < 0:
        raise CustomException("输入不能为负数!")

# 使用try-except语句捕获自定义异常
try:
    example_function(-5)
except CustomException as e:
    print("捕获到自定义异常:", e.message)

这样就定义了一个名为CustomException的自定义异常类。

我们可以在程序中使用这个自定义异常来表示特定类型的错误,并根据需要在try-except语句中捕获和处理这些异常。

自定义异常的主要优势在于它能够使程序结构更清晰,能够更准确地表示程序中可能发生的异常情况,并能够根据具体情况提供更详细的错误信息。

异常处理的层次结构

与python异常相关的关键字主要有:

异常处理的层次结构如下:

try:
    # 可能引发异常的代码块
    result = 1 / 0
except ZeroDivisionError as e:
    # 处理特定异常
    print(f"Error: {e}")
except Exception as e:
    # 处理其他异常
    print(f"Unexpected error: {e}")
else:
    # 如果没有异常发生且try中没有return时执行的代码
    print("No exceptions occurred.")
finally:
    # 无论是否发生异常都会执行的代码
    print("Finally block.")
  • 一般情况下,如果没有异常抛出以及错误,代码会执行的顺序是:try中代码块 =》else中代码块 =》finally中的代码块。
  • 如果try中抛出了异常或者错误,此时执行的顺序是:try中的代码块 =》except捕获的异常代码块 =》finally中的代码块。

finally块中的代码无论是否引发异常都会执行。通常用于确保资源的释放或清理操作。

如何捕获多个异常?

在Python中,我们可以使用多个except分别捕获不同类型的异常。

try:
    # 一些可能会引发异常的操作
    file = open('filename.txt', 'r')
    result = 10 / 0  # 这会引发一个 ZeroDivisionError
except FileNotFoundError as e:
    print(f"文件未找到错误: {e}")
except ZeroDivisionError as e:
    print(f"除以零错误: {e}")

在这个例子中,我们使用了两个except块,分别来捕获FileNotFoundError和ZeroDivisionError这两种不同类型的异常。当try块中的代码抛出任一种异常时,对应类型的except块就会捕获并处理该异常。

另一种方法是通过元组的方式一次性捕获多个异常类型。

try:
    # 一些可能会引发异常的操作
    file = open('filename.txt', 'r')
    result = 10 / 0  # 这会引发一个 ZeroDivisionError
except (FileNotFoundError, ZeroDivisionError) as e:
    print(f"发生错误: {e}")

在这个例子中,我们将多个异常类型放在一个元组中作为一个捕获异常的参数,这样就可以在一个except块中捕获多种类型的异常。

无论是逐个指定except块,还是使用一个元组指定多个异常类型,都可以帮助我们清晰地捕获并处理不同类型的异常。

异常的抛出

有些时候,我们可能需要主动抛出异常,比如:

  • 参数不符合我们设定的校验规则。
  • 捕获了异常,但是需要wrap一层更详细的报错或提示信息后继续抛出以方便上层处理。

在Python中,要手动抛出异常,可以使用raise语句。

def divide(x, y):
    if y == 0:
        raise ZeroDivisionError("除数不能为 0")
    return x / y

try:
    result = divide(10, 0)
    print(result)
except ZeroDivisionError as e:
    print(f"发生除以零错误:{e}")

在这个例子中,divide函数用于计算两个数相除,如果除数为0,则抛出一个ZeroDivisionError异常。在try块中调用了divide函数,并在except块中捕获了ZeroDivisionError异常。当除数为0时,divide函数会抛出异常,并且在except块中的代码会被执行。

通过raise语句,我们可以手动引发特定类型的异常,将其传播到调用栈中,并在需要的地方进行捕获和处理。

自定义异常

自定义异常通常被用于特定的情况或者错误类型,以便能够更清晰地识别和处理特定类型的问题。

以下是一个简单的自定义异常的示例:

class CustomException(Exception):
    def __init__(self, message="这是一个自定义异常"):
        self.message = message
        super().__init__(self.message)

在这个例子中,我们创建了一个名为CustomException的自定义异常类,它继承自Python的内置异常类Exception。我们定义了它的__init__方法来初始化异常的消息,并调用了父类的__init__方法以设置异常消息。

接下来,我们可以使用这个自定义异常类来抛出异常,并在需要捕获它的地方进行处理:

def some_function(x):
    if x < 0:
        raise CustomException("输入不能为负数")

try:
    some_function(-1)
except CustomException as e:
    print(f"捕获到了自定义异常:{e}")

在这个示例中,some_function函数根据输入的值是否小于0来抛出我们定义的CustomException自定义异常。在try...except块中,我们捕获并处理了这个自定义异常。

通过创建自定义异常类,您可以更好地组织和标识程序中特定类型的错误,从而实现更清晰、更易于维护的异常处理机制。

异常处理(函数):碰到return我该如何

  • 如果在函数内部做异常处理,此时需要考虑return。
  • 带有finally子句的try语句内使用return语句时,finally子句始终在return声明。这确保了finally子句中的代码始终运行。

a. 无return时

from loguru import logger


def example_function(raise_error=True):
    try:
        logger.debug("在try语句块中")
        if raise_error:
            raise ValueError("主动抛出一个异常...")
        # return 1
    except:
        logger.error("在except语句块中")
        # return 2
    else:
        logger.debug("在else语句块中")
    finally:
        logger.debug("在finally语句块中")
        # return 3


example_function()
example_function(raise_error=False)

这个比较清晰,也比较容易理解。

我们看到:

  • 无异常时,会执行try里面的逻辑,然后是else里面的逻辑,最后是finally中的逻辑。
  • 异常时,会执行try里面异常前的逻辑,然后是finally中的逻辑,不会执行else中的代码。

b. 仅finally里面无return,无异常或有异常时

from loguru import logger


def example_function(raise_error=True):
    try:
        logger.debug("在try语句块中")
        if raise_error:
            raise IndexError("索引错误")
        return 1
    except:
        logger.debug("在except语句块中")
        return 2
    else:
        logger.debug("在else语句块中")
        return 2.5

    finally:
        logger.debug("在finally语句块中")
        # return 3


result = example_function()
logger.debug(f"result: {result}")

result2 = example_function(raise_error=False)  # 不会执行except语句块
logger.debug(f"result2: {result2}")

这个我们可能第一感觉是会直接报错,其实不然。不过pycharm中确实飘红了~

然后我们执行,我们看到这种情况下else中的逻辑如意想的一样,并不会执行:

这个else真的好“鸡肋”啊。

c. 都有return,无异常或有异常时

from loguru import logger


def example_function(raise_error=True):
    try:
        logger.debug("在try语句块中")
        if raise_error:
            raise IndexError("索引错误")
        return 1
    except:
        logger.debug("在except语句块中")
        return 2
    else:
        logger.debug("在else语句块中")
        return 2.5

    finally:
        logger.debug("在finally语句块中")
        return 3


result = example_function()
logger.debug(f"result: {result}")

result2 = example_function(raise_error=False)  # 不会执行except语句块
logger.debug(f"result2: {result2}")

可以看到,无论是否异常,也都不会执行else中的逻辑~

  • 无异常时,因为try中return了,所以不会执行else子句;
  • 有异常时,因为try中raise了异常,所以不会执行else子

有异常或无异常时,分别走完try或except中的逻辑,最终都会执行finally中的逻辑,并且因为finally中存在return,返回值为finally的return结果。

d. 只有else中有return时

from loguru import logger


def example_function(raise_error=True):
    try:
        logger.debug("在try语句块中")
        if raise_error:
            raise IndexError("索引错误")
        # return 1
    except:
        logger.debug("在except语句块中")
        # return 2
    else:
        logger.debug("在else语句块中")
        return 2.5

    finally:
        logger.debug("在finally语句块中")
        # return 3


result = example_function()
logger.debug(f"result: {result}")

result2 = example_function(raise_error=False) 
logger.debug(f"result2: {result2}")
  • 异常的情况下,执行了try未异常代码 => except => finally的逻辑。
  • 无异常的情况下,执行了try => else => finally的逻辑,并执行了else中的return。

e. 只有else和finally中有return时

#!usr/bin/env python
# -*- coding:utf-8 _*-
# __author__:lianhaifeng
# __time__:2024/3/28 23:29
from loguru import logger


def example_function(raise_error=True):
    try:
        logger.debug("在try语句块中")
        if raise_error:
            raise IndexError("索引错误")
        # return 1
    except:
        logger.debug("在except语句块中")
        # return 2
    else:
        logger.debug("在else语句块中")
        return 2.5

    finally:
        logger.debug("在finally语句块中")
        return 3


result = example_function()
logger.debug(f"result: {result}")

result2 = example_function(raise_error=False)  # 不会执行except语句块
logger.debug(f"result2: {result2}")

警告警告:请尽量避免在finally中return!

异常处理的最佳实践

只做精准捕获异常

  • 始终只捕获那些潜在抛出异常的特定代码块。
  • 在捕获异常时,力求精确匹配具体的异常类型,避免笼统地使用通用的Exception。

这样可以防止隐藏其他问题,并使代码更具可读性。

file = None
try:
    file = open('hello.py', 'rb')
    content = file.read()
    # 这里可以有其他可能引发异常的操作
except FileNotFoundError:
    print("文件未找到异常!")
except PermissionError:
    print("权限异常!")
except Exception as e:
    print("发生了一个异常:", e)
finally:
    if file is not None:
        file.close()

异常信息的记录

在处理异常时,最好记录异常的信息以便后续排查问题。可以使用标准库中的logging、traceback模块或第三方库如:loguru。

import logging
import traceback
from loguru import logger

try:
    # 代码块
    1 / 0
except Exception as e:
    traceback.print_exc()
    logging.exception("发生了异常:%s", e)
    logger.error(f"发生了异常:{e}")
    logger.exception(f"发生了异常:{e}")

慎用或不用else,避免在finally里return

在一些情况下,else可以被完全避免,通过在try块中放置需要运行成功的代码,以避免引入不必要的逻辑复杂度。

在finally中使用return可能使代码变得难以维护。由于finally中的代码总是会执行,因此使用return可能会导致不符合预期的行为,降低代码的可维护性。

异常链的传递(raise…from)

异常链传递指的是当一个异常捕获并处理后,可以选择重新引发(re-raise)同一个异常或者另一个异常,并且保留原始异常的信息。

这种做法可以保留异常的上下文信息,使得调试和排查问题更加方便。

在Python中,可以通过在异常处理程序中使用raise ... from语句来实现异常链的传递。例如,可以在捕获到某个异常后,重新引发同一个异常或者包装成新的异常并引发。

以下是一个示例,演示了异常链的传递:

try:
    # 可能会引发异常的代码
    x = 10 / 0
except ZeroDivisionError as original_exception:
    # 捕获并处理异常
    # 引发新的异常,保留原始异常的信息
    raise ValueError("除数不能为零") from original_exception

在上面的代码中,原始的ZeroDivisionError异常被捕获,并且使用raise ... from语句重新引发成ValueError异常,同时保留了原始异常的信息。这样做的好处是,即使异常被重新引发了,我们仍然可以通过异常链追踪到原始异常的信息,从而更好地理解异常的发生原因。

自定义异常(主动抛出异常raise)

在Python中,自定义异常类通常是通过继承内置的Exception类或其子类来创建的。

通过自定义异常类,我们可以为特定的错误情况提供更具有描述性和语义化的异常类型,从而更好地组织和处理异常情况。

class CustomException(Exception):
    def __init__(self, message):
        self.message = message

    def __str__(self):
        return self.message

def divide(a, b):
    if b == 0:
        raise CustomException("除数不能为零")
    return a / b

# 使用自定义异常
try:
    result = divide(10, 0)
except CustomException as e:
    print("捕获到自定义异常:", e)

在上面的示例中,我们创建了一个名为CustomException的自定义异常类,它继承自内置的Exception类。在异常类中,我们定义了__init__方法来初始化异常对象,并传入一个错误信息。另外,我们还重写了__str__方法,以便在打印异常对象时输出错误信息。

在divide函数中,我们检查除数是否为零,如果除数为零,则抛出自定义的CustomException异常,并传入相应的错误信息。在异常处理块中,我们捕获并处理自定义异常,并打印出异常信息。

通过自定义异常,我们可以更好地组织和处理程序中可能出现的特定错误情况,提高了代码的可读性和可维护性。同时,它也使得程序的错误处理更加灵活和精确。

借助上下文管理器处理异常

异常处理不应该喧宾夺主!

上下文管理器是一种与with语句协同工作的特殊Python对象,它能够极大地简化异常处理流程,提升代码的优雅性和可读性。

我们来看一下python工匠里的例子:

def upload_avatar(request):
    """用户上传新头像"""
    try:
        avatar_file = request.FILES['avatar']
    except KeyError:
        raise error_codes.AVATAR_FILE_NOT_PROVIDED

    try:
       resized_avatar_file = resize_avatar(avatar_file)
    except FileTooLargeError as e:
        raise error_codes.AVATAR_FILE_TOO_LARGE
    except ResizeAvatarError as e:
        raise error_codes.AVATAR_FILE_INVALID

    try:
        request.user.avatar = resized_avatar_file
        request.user.save()
    except Exception:
        raise error_codes.INTERNAL_SERVER_ERROR
    return HttpResponse({})

类似代码在座的各位可能没少写~

这是一个处理用户上传头像的视图函数。这个函数内做了三件事情,并且针对每件事都做了异常捕获。如果做某件事时发生了异常,就返回对用户友好的错误到前端。

这样处理无可厚非,只是大量的try-except在主处理流程里使得我们一时间很难提炼出代码的核心逻辑,也不够pythonic。

利用上下文管理器就能够使得我们的代码更加清晰可读,具体操作是:

class raise_api_error:
    """captures specified exception and raise ApiErrorCode instead

    :raises: AttributeError if code_name is not valid
    """
    def __init__(self, captures, code_name):
        self.captures = captures
        self.code = getattr(error_codes, code_name)

    def __enter__(self):
        # 该方法将在进入上下文时调用
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # 该方法将在退出上下文时调用
        # exc_type, exc_val, exc_tb 分别表示该上下文内抛出的
        # 异常类型、异常值、错误栈
        if exc_type is None:
            return False

        if exc_type == self.captures:
            raise self.code from exc_val
        return False

接口的主要逻辑就变成下面这样:

def upload_avatar(request):
    """用户上传新头像"""
    with raise_api_error(KeyError, 'AVATAR_FILE_NOT_PROVIDED'):
        avatar_file = request.FILES['avatar']

    with raise_api_error(ResizeAvatarError, 'AVATAR_FILE_INVALID'),\
            raise_api_error(FileTooLargeError, 'AVATAR_FILE_TOO_LARGE'):
        resized_avatar_file = resize_avatar(avatar_file)

    with raise_api_error(Exception, 'INTERNAL_SERVER_ERROR'):
        request.user.avatar = resized_avatar_file
        request.user.save()
    return HttpResponse({})

简单明了了不少。


contextlib模块也提供了一些工具,用于支持与上下文管理器相关的操作。虽然contextlib模块本身并不直接用于异常处理,但它提供了一些用于创建上下文管理器的实用程序,这些上下文管理器可以帮助简化异常处理。

我们使用contextlib.contextmanager装饰器来创建一个简单的上下文管理器,用于捕获特定类型的异常:

import contextlib
from loguru import logger


@contextlib.contextmanager
def handle_file_exceptions():
    try:
        yield
    except FileNotFoundError as e:
        logger.exception(f"File not found error: {e}")
    except PermissionError as e:
        logger.exception(f"Permission error: {e}")


# 使用上下文管理器
with handle_file_exceptions():
    with open('nonexistent_file.txt', 'r') as file:
        content = file.read()

在这个例子中,handle_file_exceptions函数使用contextlib.contextmanager装饰器转变为一个上下文管理器。在调用yield之前的代码段被看作是上下文管理器的__enter__方法,在yield之后的代码段被看作是上下文管理器的__exit__方法。在__exit__方法中,我们捕获了FileNotFoundError和PermissionError,并打印了相应的错误信息。

虽然contextlib模块的主要目的是创建上下文管理器,但它也可以与异常处理一起使用,使得在特定上下文情境中进行异常处理变得更加方便和简洁。

使用装饰器处理异常

异常处理装饰器是另一种用于简化异常处理逻辑的技术,它可以在函数调用时自动捕获并处理异常,从而减少重复代码量,提高代码的可维护性和可读性。

from loguru import logger


def handle_exception(exception_handler):
    def decorator(func):
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                return exception_handler(e, *args, **kwargs)

        return wrapper

    return decorator


# 自定义异常处理函数
def custom_exception_handler(exception, *args, **kwargs):
    # 这里可以进行自定义的异常处理逻辑
    if isinstance(exception, ZeroDivisionError):
        logger.error(f"捕获到异常:{exception}")

    return None  # 返回处理结果或者 None


# 使用异常处理装饰器
@handle_exception(custom_exception_handler)
def divide(a, b):
    return a / b


result = divide(10, 1)
logger.info(f"result: {result}")
# 调用带有异常处理装饰器的函数
result2 = divide(10, 0)
logger.info(f"result2: {result2}")

使用异常处理装饰器可以使代码更加简洁、清晰,并且能够提高代码的复用性和可维护性。
注意: 异常处理装饰器的具体实现可以根据实际需求进行定制和扩展,以满足不同场景的需求。

from functools import wraps
from typing import Callable, Dict, Type
from loguru import logger


class TryDecorator:
    def __init__(self):
        self.exception_handlers: Dict[Type[Exception], Callable] = {}

    def try_(self, func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                handler = self.get_exception_handler(type(e))
                if handler is None:
                    raise e
                return handler(func, e, *args, **kwargs)

        return wrapper

    def except_(self, *exceptions: Type[Exception]) -> Callable:
        def decorator(handler: Callable) -> Callable:
            for exception in exceptions:
                self.exception_handlers[exception] = handler
            return handler

        return decorator

    def get_exception_handler(self, exception_type: Type[Exception]) -> Callable:
        for exception, handler in self.exception_handlers.items():
            if issubclass(exception_type, exception):
                return handler
        return lambda *args, **kwargs: None  # 返回一个默认的异常处理函数,可以根据需要修改


try_decorator = TryDecorator()


@try_decorator.try_
def my_function():
    logger.info(1 / 0)
    logger.info('hello world')


@try_decorator.try_
def my_function2():
    raise IndexError("主动抛出了IndexError")


@try_decorator.except_(ZeroDivisionError)
def handle_zero_division_error(func: Callable, exception: Exception, *args, **kwargs):
    logger.error(f"ZeroDivisionError in function {func.__name__}: {exception}")


@try_decorator.except_(IndexError)
def handle_zero_division_error(func: Callable, exception: Exception, *args, **kwargs):
    logger.error(f"IndexError in function {func.__name__}: {exception}")


if __name__ == '__main__':
    my_function()
    my_function2()

让你的函数返回一些有意义的、有类型的、安全的东西!

对于python函数的异常处理往往令我们比较的头疼。

比如,可能你的函数的正常流程是返回一个字符串,异常的时候是直接raise抛出呢还是返回一个特定值?这是一个问题!

传统的异常处理机制依赖于在函数内部抛出异常,然后在调用函数的地方进行捕获和处理。
这种方式可能会导致代码分散、难以维护,同时将错误处理的责任分散到多个地方。异常可能会中断程序的正常流程,使得代码复杂和难以预测。

聪明如你,可能自己封装一个统一的结构化响应。

统一响应:谁弄(异)哭(常)的,谁就要负责哄(包装)~

不过我要告诉你的是,使用returns模块可以将函数的结果值进行封装,这样就可以更灵活地处理异常情况。

returns模块提供了Success和Failure两种类型,分别代表函数成功执行和出现错误。这就使得函数返回的结果更加明确,可以更容易地进行组合、转换和处理。

安装:

pip install returns

我们改写下使用装饰器处理异常部分的示例代码:

from typing import Callable
from returns.result import Success, Failure, Result
from loguru import logger
from functools import wraps


class TryDecorator:
    def __init__(self):
        # 初始化异常处理程序字典
        self.exception_handlers = {}

    def try_(self, func: Callable) -> Callable[..., Result]:
        @wraps(func)
        def wrapper(*args, **kwargs) -> Result:
            try:
                result = func(*args, **kwargs)
                # 如果返回结果已经是 Result 类型,则直接返回
                if isinstance(result, Result):
                    return result
                # 否则将返回结果包装成 Success 类型
                return Success(result)
            except Exception as e:
                # 获取对应异常类型的处理函数
                handler = self.get_exception_handler(type(e))
                if handler is None:
                    # 如果没有找到对应的异常处理函数,则直接返回 Failure
                    return Failure(e)
                # 调用对应异常处理函数处理异常
                return handler(func, e, *args, **kwargs)

        return wrapper

    def except_(self, *exceptions: type) -> Callable:
        def decorator(handler: Callable) -> Callable:
            # 为每个异常类型关联对应的处理函数
            for exception in exceptions:
                self.exception_handlers[exception] = handler
            return handler

        return decorator

    def get_exception_handler(self, exception_type: type) -> Callable:
        # 获取对应异常类型的处理函数
        for exception, handler in self.exception_handlers.items():
            if issubclass(exception_type, exception):
                return handler
        # 如果没有找到对应异常的处理函数,则返回一个默认的异常处理函数,可以根据需要修改
        return lambda *args, **kwargs: None


try_decorator = TryDecorator()


@try_decorator.except_(ZeroDivisionError)
def handle_zero_division_error(func: Callable, exception: Exception, *args, **kwargs) -> Result:
    # 处理 ZeroDivisionError 异常,并返回 Failure
    logger.error(f"ZeroDivisionError in function {func.__name__}: {exception}")
    return Failure(exception)


@try_decorator.except_(IndexError)
def handle_index_error(func: Callable, exception: Exception, *args, **kwargs) -> Result:
    # 处理 IndexError 异常,并返回 Failure
    logger.error(f"IndexError in function {func.__name__}: {exception}")
    return Failure(exception)


@try_decorator.try_
def my_function(a, b):
    return Success(a / b)


@try_decorator.try_
def my_function2():
    raise IndexError("主动抛出了IndexError")


if __name__ == '__main__':
    # 测试 try_decorator 的 try_ 方法
    # result1 = try_decorator.try_(my_function)(10, 1)
    result1 = my_function(10, 1)
    if isinstance(result1, Success):
        logger.info(f"没有异常:{result1}, result1: {result1.unwrap()}")

    # 测试 ZeroDivisionError 异常处理
    result2 = my_function(10, 0)
    if isinstance(result2, Failure):
        logger.info(f"发生异常:{result2}, result2: {result2.failure()}")

    result3 = my_function2()
    if isinstance(result3, Failure):
        logger.error(f"Failed with: {result3.failure()}")

是不是清爽了许多,输出结果为:

  • returns模块促进了函数式编程风格的使用。
  • 使用returns模块可以更容易地编写测试,因为你可以明确定义函数的行为并检查所得的结果。
  • 使用返回值进行异常处理能够提供更多的灵活性和可预测性。通过返回值传递错误信息,调用者可以决定是否要处理错误,以及如何处理错误。

更多returns特性请阅读:
github: https://github.com/dry-python/returns

更多场景举例

只做示例介绍,不一定遵循最佳实践。

基本的异常处理重试装饰器

下面是一个基本的异常处理重试装饰器的示例:

import time
from functools import wraps
from typing import Callable


def retry_on_exception(max_retries: int, delay: float = 0.1):
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"Exception caught: {e}. Retrying...")
                    time.sleep(delay)
            raise Exception(f"Failed after {max_retries} retries")

        return wrapper

    return decorator


# 使用示例
@retry_on_exception(max_retries=3)
def potentially_unstable_function():
    import random
    if random.randint(1, 10) < 8:
        raise ValueError("Random error")
    else:
        return "Success"


if __name__ == "__main__":
    result = potentially_unstable_function()
    print(result)

在这个示例中,retry_on_exception是一个装饰器工厂函数,它接受最大重试次数和延迟作为参数。它返回一个装饰器,该装饰器会捕获被装饰函数可能抛出的异常,并在遇到异常时进行重试。

被装饰的函数potentially_unstable_function可能会抛出随机异常。我们使用retry_on_exception装饰器装饰它,以便在遇到异常时进行重试,最多重试三次。

你可以根据需要调整max_retries和delay参数来控制重试的次数和重试之间的延迟。

异常优雅重试

其实,很多轮子已经是卷中卷了,我们大可使用优雅的retrying模块。

pip install retrying

使用也很简单:

from retrying import retry
import random
from loguru import logger

attempt = 0


# 重试装饰器
@retry(stop_max_attempt_number=3, wait_fixed=100)  # 最多重试3次,每次间隔100ms
def potentially_unstable_function():
    global attempt
    attempt += 1
    if random.randint(1, 10) < 8:
        logger.error(attempt)
        raise ValueError("Random error")
    else:
        return "Success"


if __name__ == "__main__":
    result = potentially_unstable_function()
    logger.info(result)

这里为了演示确实最大重试了stop_max_attempt_number=3,加了attempt,不然代码只会更少!

超过3次时报错如下:

总结

恭喜毕业!异常处理是编写稳健、可维护Python代码的重要组成部分。以下是异常处理的关键点总结:

  1. 捕获最具体的异常:为了更好地理解和处理错误,请捕获最具体的异常类型。

  2. 不要使用单独的except处理所有异常:避免使用空的except块,因为它们会捕获所有异常,包括意外的情况,会增加调试的困难。

  3. 谨慎使用else和finally块:else块用于在没有异常时执行特定代码,finally块用于确保资源释放或清理操作。

  4. 记录异常信息:捕获异常后,通常应记录异常信息,以便诊断和修复问题。

  5. 合理使用自定义异常: 当您的应用程序遇到特定异常条件时,建议创建一个自定义异常类,可以更清晰地表达和有效处理这些情况。

  6. 避免不必要的异常处理:不应将异常处理用于预期的控制流,而应只在真正可能发生异常的地方使用它。

  7. 使用装饰器,上下文管理器,returns库使你的异常处理更加的pythonic。

最后

如果你觉得文章还不错,请大家点赞、分享、留言,因为这将是我持续输出更多优质文章的最强动力!

参考

https://returns.readthedocs.io/en/latest/pages/result.html

RT-DETR改进策略【独家融合改进】| SPD-Conv+PPA 再次提升模型针对小目标的特征提取能力 SPD-Conv是一种新的 CNN 构建模块,用于替代传统 CNN 架构中使用的步长卷积(strided convolution)和池化(pooling)层,它由空间到深度(Space-to-depth,SPD)层和非步长卷积(non - strided convolution)层组成。 阅读详情

相关推荐

Python的WNTR库---基于供水网络

WNTR使用小结(一) 本人最近主要再做设施网络的内容,主要聚焦供水管网(water distribution network,WDN),在做实验中主要用的包为WNTR,下载连接: 链接: WNTR.最好安装最新版本 如果有做这方面研究的小伙伴们需要测试数据可以直接从WNTR实例数据中下载,其中提供了四种规模大小的管网,格式为.inp,可以搭配Epanet使用。如果需要更多示例数据可私信我! 以下内容全部为本人自己探索的小心得,希望能给你带来一点点帮助,如果不够详尽可以自行参阅官方文档:链接: 官方文档.

pushhy的博客 4931

[Python]读取csv文件OSError解决方法

今天这篇推文,就是给大家总结一下我用pandas读取csv文件遇到的坑。 一直处理csv文件都是用pandas的read_csv函数读取csv文件之后处理,都没碰到过什么问题。But,But今天帮朋友处理csv文件时,读取一直错误。 首先文件长这样,(文件名为:重庆全量.csv): 首先,导入pandas包后,直接用read_csv函数读取报错OSError,如下: 运行之后没有报错,正在我...

Asher117的博客 6512

基因家族快速鉴定指南:Windows平台下的HMMER实战解析

本文详细解析了在Windows平台下使用HMMER工具进行基因家族快速鉴定的完整实战流程。通过手把手指导,从软件获取、数据准备到模型构建与序列搜索,帮助生物信息学新手无需Linux环境,即可高效完成bZIP转录因子等家族的鉴定工作,大幅降低分析门槛。

weixin_29255731的博客 453

python异常处理

快捷键复习:ctrl+/多行快速注释;选中内容按特定快捷键,可直接添加引号及括号等符号 1.有预料的处理不存在的文件excpet 错误类型: print("---test---1--") f=open("123.txt","r") #用只读打开了一个不存在的文件导致报错 print("---test---2--")#这句代码不会被执行 FileNotFoundError: [Errno 2] No such file or directory: '123.txt' FileNotFoundError:

m0_60700888的博客 730

AI 技术新手入门与实战指南

仅仅能对话还不够,实际应用中,我们往往需要让 AI 处理特定的业务数据。比如,从日志文件中提取错误码,或者将非结构化的用户反馈整理成 JSON 格式。这时候,自定义输入和严格的结果验证就显得尤为重要。假设我们需要让 AI 分析一段服务器日志,并只返回错误的级别和简述。请分析以下日志片段,提取所有 ERROR 级别的信息。要求输出格式为 JSON 列表,包含 'timestamp' 和 'message' 两个字段。不要输出任何多余的解释文字。日志内容: {

2600_94936330的博客 2680

Python通用编程规范-09 安全编程规范

1.9 安全编程规范 1.9.1 异常行为 1.9.1.1 禁止抑制或者忽略已检查异常 【说明】: 编码人员常常会通过一个空的或者无意义的catch块来抑制捕获的已检查异常。每一个catch块都应该确保程序只会在继续有效的情况下才会继续运行下去。因此,catch块必须要么从异常情况中恢复,要么重新抛出适合当前catch块上下文的另一个异常以允许最邻近的外层try-catch语句块来进行恢复工作。异常会打断应用原本预期的控制流程。例如,try块中位于异常发生点之后的任何表达式和语句都不会被执行。因此,异常必须

zhao12501的博客 2871

6.错误与异常

程序有可能出现错误与异常 1.通过try,except处理。 try: 业务语法块 except 异常类型: 处理异常语法块 2.抛出异常 raise Exception("") 3.finally子句 无论是否有异常或者是否捕捉异常,finally子句都会执行 ...

鹏哥哥Aaa 321

5分钟搞定三网话费余额查询:手把手教你用PHP+HTML搭建查询系统(含API调用避坑指南)

本文详细介绍了如何使用PHP+HTML快速搭建三网话费余额查询系统,涵盖API调用避坑指南、前端UI设计及性能优化。通过统一处理移动、电信、联通三大运营商接口差异,实现高可用查询功能,并提供安全防护与生产部署建议,帮助开发者高效构建稳定可靠的话费查询工具。

weixin_29016833的博客 256

python异常处理关键字_python入门学习异常处理类型

前言异常是指在程序执行过程中发生的一个事件,影响了程序的正常执行,例如:0做了分母。异常是Python对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。在Java语言中我们知道异常的常用结构为try catch组成,有时也会用到finally,那么在python中异常是如何来处理的呢?python中的异常处理1.python异常简单语法python异常的简单语...

weixin_39605521的博客 696

【WorkBuddy专栏09】WorkBuddy多模态能力深度揭秘

【WorkBuddy专栏09】AI不止会聊天——WorkBuddy多模态能力深度揭秘

AI人人懂 2104

2026年美赛数学建模竞赛 MCM B题:使用太空电梯系统建立月球殖民地 问题一 月球殖民地材料运输的三种场景建模 详细解题思路和代码

如果运输系统不能完美工作(例如,系绳摆动、火箭失败、电梯故障等),你的解决方案在多大程度上会发生变化?技术可行性:三种方案在技术上都是可行的,但挑战不同 经济性:混合方案在长期视角下最具成本效益 时间效率:并行运输可以显著缩短总建设时间环境影响:太空电梯的环境优势明显,混合方案可平衡需求风险分散:混合方案提供了天然的风险分散机制

YOLO 615

python中zerodivisionerror是什么意思-python里的raise是什么意思

我们在开发当中经常会遇到一些未知或已知的错误,比如用requests发送请求时可能会出现异常,或者某个程序计算时出现分母为0的情况。面对各种奇形怪状的错误,高级语言都会有一个抛出错误的机制。在Python中,他就是raise了。raise()raise是什么意思呢?raise是用raise语句来引发一个异常。异常/错误对象必须有一个名字,且它们应是Error或Exception类的子类。抛出异常和...

weixin_37988176的博客 949

1. 迈进 SAP UI5 开发世界的第一步 - Hello World

SAP UI5 是一个用于开发企业级 Web 应用的前端框架,基于 HTML 和 JavaScript. 作为教程的第一步,我们需要了解最基础的 HTML 知识。 请大家在电脑上任意新建一个文件夹,将其命名,比如我取的名称为 Walkthrough. 进入 Walkthrough 文件夹,再新建一个文件夹名叫 01,代表本教程的第一个步骤。 进入 01 文件夹,新建一个 webapp 文件夹。这个名称是 web 应用约定俗成所使用的,大家都倾向于用这个名称,当然你要是想用其他名称也没有问题。......

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 8999

【Python】异常处理:从基础到进阶

掌握并善用异常处理技巧,将使我们的代码更加优雅、可靠,真正能够应对复杂多变的实际应用场景。

Yui_的博客 6345

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Python异常处理:从基础到进阶

异常(Exception)是指在程序运行过程中发生的错误或异常情况,这些错误会打断程序的正常执行流程。Python中的异常是对象,表示一个错误或异常情况。除了Python内置的异常类,我们还可以定义自己的异常类。自定义异常类需要继承内置的Exception类。try:# 可能会引发自定义异常的代码raise MyCustomError("这是一个自定义异常!")print(e)

这家伙很懒,什么都没有留下 839

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Python3 详解:从基础到进阶的完整指南

Python3核心特性解析:默认UTF-8编码、print()函数、/返回浮点数;统一字符串类型为str和bytes;增强生成器、装饰器等高级特性;改进面向对象和异常处理。与Python2相比,关键差异包括编码、打印、除法和字符串处理等。推荐常用库如NumPy、Pandas、Flask等。迁移指南建议使用2to3工具和future库,注意字符串处理和依赖升级。学习资源推荐官方文档、《流畅的Python》等书籍和在线课程。

技术人集结地 1413

Python进阶学习指南:从新手到高手的10个实用技巧

Python进阶学习是每个Python程序员成长的关键阶段。无论你是刚刚入门Python的新手,还是希望提升编程技能的开发者,掌握Python进阶技巧都能让你在编程道路上走得更远。本指南将为你揭秘10个实用的Python进阶技巧,帮助你从基础迈向高级,成为真正的Python高手!🚀 ## 1. 深入理解函数参数传递 在Python进阶学习中,函数参数的处理是基础中的基础。[args_kwar

gitblog_01081的博客 945

Python 编程实战指南:从基础到进阶

在 Python 中,使用def关键字定义一个函数,函数名后跟着圆括号,用来传递参数。name } " # 调用函数 message = greet("Alice") print(message)# 调用函数在现代计算机系统中,我们经常需要同时执行多个任务,尤其是在进行 I/O 密集型操作或需要提高程序执行效率时。并发编程是指多个任务在同一时间段内执行的能力。

weixin_43199439的博客 1814

Python异常处理:从入门到精通的实用指南

在Python编程中,异常处理是提升代码健壮性和用户体验的关键技能。无论是初学者还是进阶开发者,掌握异常处理都能让你的程序在面对错误时从容应对,而不是直接崩溃。本文将从基础入手,详细讲解异常的概念、try-except语句的用法以及自定义异常的创建,帮助你构建系统的知识体系,提升代码质量。无论你是想快速上手异常处理,还是希望深入探索其高级应用,这篇文章都能满足你的需求。通过通俗易懂的语言、清晰的代码示例和实际案例,你将学会如何让代码更可靠、更专业。关键词。

吴师兄大模型的博客 1462

Python编程学习指南:从入门到精通的实用示例

Python作为当下最流行的编程语言之一,以其简洁的语法、强大的功能和丰富的生态系统吸引了无数开发者。无论你是编程新手还是希望提升Python技能的开发者,本文都将为你提供一条清晰的学习路径,并辅以实用的代码示例,帮助你快速掌握Python编程。Python是一门既适合初学者入门又值得深入研究的编程语言。通过本文的学习路径和实用示例,相信你已经对Python编程有了全面的认识。记住,编程学习最重要的是实践,多写代码,多思考,多解决问题。第一部分:Python基础学习。从Python官网下载最新版本。

Python_trys的博客 1163

全套百度云教程Python基础进阶项目篇:全面掌握Python实战能力

全套百度云教程Python基础进阶项目篇:全面掌握Python实战能力 【下载地址】全套百度云教程Python基础进阶项目篇 这套Python学习资源为不同层次的学习者提供了全面的教程,涵盖基础、进阶和实战项目三大部分。基础篇从Python语言概述、环境搭建到文件操作与异常处理,帮助初学者快速入门;进阶篇深入讲解面向对象...

gitblog_06756的博客 1072

【Python 进阶】2 小时掌握实用进阶技巧:从数据处理到自动化办公(附实战项目)

Python进阶:从基础到高效办公 本文针对已掌握Python基础的学习者,提供进阶技巧与实战指导,帮助提升代码效率与健壮性。 核心内容: 数据结构高效用法:列表推导式、字典合并、集合去重,减少冗余代码。 异常处理进阶:try-except-else-finally完整结构,自定义异常处理业务逻辑。 模块化与面向对象:规范导入、自定义模块复用代码,类与继承实现复杂功能。 实战项目:批量合并Excel文件,筛选数据并生成报表,结合异常处理与日志记录。 适用场景:办公自动化(Excel/Word处理)、数据清洗

Qq090102的博客 1111

【第五章】Python 异常处理全解析:从基础到进阶

本文系统讲解了Python异常处理机制,从基础概念到高级技巧全面解析。完整展示了异常处理从基础到进阶的应用场景。

Himan21的博客 1039

Python文件操作与异常处理全面指南:从基础到进阶技巧

在 Python 编程中,文件操作和异常处理是基础且至关重要的技能,它们直接影响到程序的稳定性和健壮性。无论是进行文件读写,还是在程序运行过程中捕获并处理错误,掌握这些技能是每个 Python 开发者必不可少的能力。同时,调试技巧也是确保代码正确性和高效运行的重要工具。编写 Python 程序时,我们不可避免地会遇到文件读写操作,且常常会碰到各种异常。了解并正确应用文件操作模式、掌握异常处理机制、熟练使用调试工具,能让你更高效地解决问题,避免代码崩溃,提升开发体验。

吴师兄大模型的博客 1194

从零开始:Python语言进阶之异常处理

本文介绍了Python异常处理的核心知识。首先解释了异常的概念,列举了常见的异常类型(如ZeroDivisionError、FileNotFoundError等)。重点讲解了try-except语句的基础用法,包括捕获多个异常类型的方法。随后介绍了else和finally子句的应用场景,强调finally子句在资源释放中的重要性。文章还讲解了如何自定义异常类以适应业务需求。最后针对常见问题(如捕获所有异常、异常丢失等)给出了解决方案,并指出合理使用条件判断能减少不必要的异常处理开销。全文强调异常处理是编写健

玉笥寻珍的博客 2823

Python 异常处理完全指南

Python 异常处理

YAnt的博客 2359
上一篇: Python方法链调用的优雅之处
下一篇: 【逆向百例】百度翻译js逆向
海哥python
博客等级 码龄9年 1487粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海哥python

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值