Python上下文管理和with

有一些任务,可能事先需要设置,事后做清理工作。可以通过try...except...finally来完成,如:

python 复制代码
file = open('./test.txt', 'r')
try:
    context = file.read()
    print(context)
except Exception as e:
    print(e)
finally:
    file.close()

上面长长的一段代码,写的很复杂麻烦。

python使用with语句对try...except...finally进行了大幅的简化,并且使过程更容易理解。上面的过程使用with语句可以改写为:

python 复制代码
with open('./test.txt', 'r') as file:
    context = file.read()
    print(context)

with 语句使代码更清晰、更具可读性, 它简化了文件流等公共资源的管理。在处理文件对象时使用 with 关键字是一种很好的做法。

上下文管理器

with 语句实现原理建立在上下文管理器之上。上下文管理器是一个实现 enterexit 方法的类。使用 with 语句确保在嵌套块的末尾调用 exit 方法。这个概念类似于 try...finally 块的使用。

python 复制代码
with open('./test.txt', 'r') as file:
    context = file.read()
    print(context)

print(hasattr(file, '__enter__')) #true
print(hasattr(file, '__exit__'))  #true

在文件对象中定义了 enterexit 方法,即文件对象也实现了上下文管理器,首先调用 enter 方法,然后执行 with 语句中的代码,最后调用 exit 方法。 即使出现错误,也会调用 exit 方法,也就是会关闭文件流。

python 复制代码
class MyWith():
    def __init__(self):
        print('MyWith.__init__')

    def __enter__(self):
        print('MyWith.__enter__')
        return 'enter.return'

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('MyWith.__exit__')

    def __del__(self):
        print('MyWith.__del__')

with MyWith() as w:
    print(f'...with...{w=}')

print('Game Over!')


'''
MyWith.__init__
MyWith.__enter__
...with...w='enter.return'
MyWith.__exit__
MyWith.__del__
Game Over!

'''

从上面的例子执行的顺序是:

  • 执行with as语句时,MyWith类的__init__()被调用后,enter()方法紧接着会被调用,因此首先会打印"MyWith.init
    MyWith.enter"
  • enter__函数返回的对象w,实际是__enter()方法的返回值,所以打印的内容是"...with...w='enter.return'"
  • 当with as语句执行完毕以后,__exit__函数会被调用,因此随后打印的是"MyWith.exit"
  • 最后是w被释放,打印的是析构函数输出的内容

__exit__函数的三个参数

注意到__exit__函数中除了self参数外,还有三个参数type, value, trace,这些参数用于处理异常情况。

  • exc_type:表示引发的异常类型,如果代码块成功执行完毕,则为None。
  • exc_val:表示引发的异常实例,如果代码块成功执行完毕,则为None。
  • exc_tb:表示引发的异常跟踪对象,如果代码块成功执行完毕,则为None。

下面是一个示例,演示如何使用__exit__()方法来处理异常情况:

python 复制代码
class FileManager:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None

    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is not None:
            print(f"An error occurred: {exc_val}")
            self.file.close()
        else:
            self.file.close()

# 使用上下文管理器打开文件
with FileManager("example.txt", "r") as file:
    print(file.readlines())

# 使用不存在的文件名打开文件
with FileManager("nonexistent_file.txt", "r") as file:
    print(file.readlines())

应用场景

文件操作

在Python中,文件操作通常是通过open函数来实现的,当打开文件之后,我们需要使用close方法来关闭文件,保证文件的完整性和安全性。但是在实际的开发过程中,往往会出现遗漏close方法的情况,这时候就可以使用with语句来自动关闭文件,避免出现问题。例如:

python 复制代码
with open('example.txt', 'r') as f:
  data = f.read()
  #处理文件内容

数据库操作

在Python中,数据库操作通常是通过使用DB-API来实现的,这需要我们手动打开数据库连接和关闭数据库连接,如果不小心忘记关闭数据库连接,就可能导致数据库出现问题。使用with语句可以方便地自动关闭数据库连接,避免出现问题。例如:

python 复制代码
import sqlite3

with sqlite3.connect('example.db') as conn:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM example_table')
    data = cursor.fetchall()
    #处理数据

网络编程

在Python中,网络编程通常是通过使用socket模块来实现的,当我们创建了一个socket对象之后,需要手动关闭socket对象,否则会出现问题。使用with语句可以方便地自动关闭socket对象,避免出现问题。

python 复制代码
import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
  s.connect(('www.baidu.com', 80))
  s.sendall(b'GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n')
  data = s.recv(1024)
  #处理数据
相关推荐
lifallen17 分钟前
Java Stream sort算子实现:SortedOps
java·开发语言
IT毕设实战小研20 分钟前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi43 分钟前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
mit6.8241 小时前
[1Prompt1Story] 滑动窗口机制 | 图像生成管线 | VAE变分自编码器 | UNet去噪神经网络
人工智能·python
没有bug.的程序员1 小时前
JVM 总览与运行原理:深入Java虚拟机的核心引擎
java·jvm·python·虚拟机
甄超锋1 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
cui__OaO2 小时前
Linux软件编程--线程
linux·开发语言·线程·互斥锁·死锁·信号量·嵌入式学习
鱼鱼说测试2 小时前
Jenkins+Python自动化持续集成详细教程
开发语言·servlet·php
AntBlack2 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
艾莉丝努力练剑3 小时前
【洛谷刷题】用C语言和C++做一些入门题,练习洛谷IDE模式:分支机构(一)
c语言·开发语言·数据结构·c++·学习·算法