首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python - with 语句

Python - with 语句

作者头像
小菠萝测试笔记
发布2021-09-29 16:55:59
发布2021-09-29 16:55:59
1.6K0
举报

管理外部资源的背景

  • 在编程中会面临的一个常见问题是如何正确管理外部资源,例如文件、锁和网络连接
  • 有时,程序会永远保留这些资源,即使不再需要它们,这种现象称为内存泄漏
  • 因为每次创建和打开给定资源的新实例而不关闭现有资源时,可用内存都会减少
如何正确管理资源
  • 正确管理资源通常是一个棘手的问题
  • 它需要一个设置阶段和一个清理阶段
  • 后一个阶段需要执行一些清理操作,例如关闭文件、释放锁或关闭网络连接
  • 如果忘记执行这些清理操作,那么应用程序将使资源保持活动状态,这可能会损害宝贵的系统资源,例如内存和网络带宽
数据库连接数问题
  • 最常见的数据库连接数问题
  • 使用数据库时,可能会出现程序不断创建新连接而不释放或重用它们
  • 在这种情况下,数据库后端可以停止接受新连接
  • 这可能需要管理员登录并手动终止那些陈旧的连接以使数据库再次可用
写入文件问题
  • 将文本写入文件通常是一种缓冲操作
  • 这意味着对文件调用 .write() 不会立即导致将文本写入物理文件,而是写入临时缓冲区
  • 有时,当缓冲区未满而开发人员忘记调用 .close() 时,部分数据可能会永远丢失

with 的作用

常规说法
  • with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源
  • 比如文件使用后自动关闭/线程中锁的自动获取和释放等。
官方解释
  • 仅适用于执行上下文管理器定义的方法的代码块
  • 允许对普通的 使用模式进行封装以方便地重用

try...except...finally

一句话总结

使用 with as 语句操作上下文管理器(context manager),它能够帮助我们自动分配并且释放资源

什么是上下文管理器

详细教程

with as 的基本语法

代码语言:javascript
复制
with 表达式 [as target]:
    代码块
执行顺序
  1. 调用表达式以获取上下文管理器
  2. 存储上下文管理器的 .__enter__() 和 .__exit__() 方法供以后使用
  3. 在上下文管理器上调用 .__enter__() 并将其返回值绑定到 target(如果有的话)
  4. 执行 with 代码块
  5. 当 with 代码块完成时,在上下文管理器上调用 .__exit__()

访问文件的代码演进

最基础的写法
代码语言:javascript
复制
# 1、打开文件
file = open("1.txt")

# 2、读取文件
data = file.read()

# 3、手动关闭文件
file.close()  
存在的问题

在第二步假设文件读取的时候发生异常,没有做任何处理,就不会执行第三步,导致程序可能会泄露文件描述符

使用 try...except...finally 优化
代码语言:javascript
复制
try:
    # 打开文件、读取文件
    f = open('xxx')
    data = f.read()
except Exception as e:
    # 捕获异常
    pass
finally:
    # 关闭文件
    f.close()
  • 无论是否抛出异常,最后还是会关闭文件,解决上面提到的问题
  • 但新的问题在于,代码比较冗余,而且要手动关闭文件
使用 with 优化
代码语言:javascript
复制
with open("1.txt") as file:
    data = file.read()
  • 作用和 try 写法一样
  • 优势:代码简洁,自动关闭文件,释放资源
  • with 代码块执行完后,会自动调用文件对象的 .close() 方法

支持多个上下文管理器

代码语言:javascript
复制
with open("input.txt") as in_file, open("output.txt", "w") as out_file:
     # 从 input.txt 读取内容
     # 转换内容
     # 将转换后的内容写入output.txt
    pass
等价写法
代码语言:javascript
复制
with open("input.txt") as in_file:
    with open("output.txt", "w") as out_file:
        pass

使用 pathlib.Path.open()

代码语言:javascript
复制
import pathlib

file_path = pathlib.Path("a.txt")
with file_path.open("w") as file:
    file.write("Hello, World!")
  • 由于 pathlib 提供了一种优雅、直接和 Pythonic 的方式来操作文件系统路径
  • 因此应该考虑在 with 语句中使用 Path.open() 作为 Python 中的最佳实践

捕获异常的栗子

无论何时加载外部文件的程序都应检查可能存在的问题,例如文件丢失、读写访问等

代码语言:javascript
复制
import pathlib
import logging

file_path = pathlib.Path("a.txt")
try:
    with file_path.open("w") as file:
        file.write("Hello, World!")
except OSError as error:
    logging.error("Writing to file %s failed due to: %s", file_path, error)
  • 在 with as 外层添加 try ... except 用于捕获异常
  • 如果在执行 with 期间发生 OSError,则使用日志记录错误信息

遍历目录的栗子

代码语言:javascript
复制
import os

with os.scandir(".") as entries:
    for entry in entries:
        print(entry.name, "->", entry.stat().st_size, "bytes")
  • scandir() 会返回一个支持上下文管理协议的迭代器
  • .__exit__() 将调用 scandir.close() 关闭迭代器并释放获取的资源
输出结果
代码语言:javascript
复制
__init__.py -> 178 bytes
a.txt -> 13 bytes
1_上下文管理器.py -> 2168 bytes

高精度计算

代码语言:javascript
复制
# 高精度计算
from decimal import Decimal, localcontext

with localcontext() as ctx:
    ctx.prec = 42
    res = Decimal("1") / Decimal("42")
    print(res)
输出结果
代码语言:javascript
复制
0.0238095238095238095238095238095238095238095

扩展阅读

https://realpython.com/python-with-statement/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 管理外部资源的背景
    • 如何正确管理资源
    • 数据库连接数问题
    • 写入文件问题
  • with 的作用
    • 常规说法
    • 官方解释
    • 一句话总结
  • 什么是上下文管理器
  • with as 的基本语法
    • 执行顺序
  • 访问文件的代码演进
    • 最基础的写法
    • 存在的问题
    • 使用 try...except...finally 优化
    • 使用 with 优化
  • 支持多个上下文管理器
    • 等价写法
  • 使用 pathlib.Path.open()
  • 捕获异常的栗子
  • 遍历目录的栗子
    • 输出结果
  • 高精度计算
    • 输出结果
  • 扩展阅读
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档