ChatGPT解决这个技术问题 Extra ChatGPT

python“with”语句的设计目的是什么?

我今天第一次遇到 Python with 语句。几个月来我一直在轻松使用 Python,甚至不知道它的存在!鉴于其有点模糊的地位,我认为值得一问:

Python with 语句设计用于什么用途?你用它来做什么?是否有任何我需要注意的问题,或与其使用相关的常见反模式?任何情况下使用 try..finally 比 with 更好?为什么不更广泛地使用它?哪些标准库类与之兼容?

仅作记录,Python 3 文档中的here is with
来自 Java 背景,它帮助我记住它是 Java 中相应的“资源尝试”,即使这可能不完全正确。
仅作记录,这里是 PEP-0343:python.org/dev/peps/pep-0343

T
Tamás

我相信在我之前的其他用户已经回答了这个问题,所以我只是为了完整起见添加它:with 语句通过将常见的准备和清理任务封装在所谓的上下文管理器中来简化异常处理。更多细节可以在 PEP 343 中找到。例如,open 语句本身就是一个上下文管理器,它允许你打开一个文件,只要执行是在你使用它的 with 语句的上下文中,它就可以保持打开状态,并在您离开上下文后立即关闭它,无论您是因为异常还是在常规控制流期间离开它。因此,with 语句可以以类似于 C++ 中的 RAII 模式的方式使用:某些资源由 with 语句获取,并在您离开 with 上下文时释放。一些示例是:使用 with open(filename) as fp: 打开文件,使用 with lock: 获取锁:(其中 lock 是 threading.Lock 的一个实例)。您还可以使用 contextlib 中的 contextmanager 装饰器构建自己的上下文管理器。例如,当我必须临时更改当前目录然后返回到原来的位置时,我经常使用它: from contextlib import contextmanager import os @contextmanager def working_directory(path): current_dir = os.getcwd() os.chdir(path ) try: yield finally: os.chdir(current_dir) with working_directory("data/stuff"): # do something inside data/stuff # 这里我又回到了原来的工作目录 这是另一个临时重定向 sys.stdin 的例子, sys.stdout 和 sys.stderr 到其他文件句柄并在以后恢复它们: from contextlib import contextmanager import sys @contextmanager def redirected(**kwds): stream_names = ["stdin", "stdout", "stderr"] old_streams = {} try: for sname in stream_names: stream = kwds.get(sname, None) 如果 stream 不是 None 并且 stream != getattr(sys, sname): old_streams[sname] = getattr(sys, sname) setattr(sys, sname, stream) 最终产生:对于 sname,在 old_streams.iteritems() 中流: setattr(sys, sname, stream) 与 redir ected(stdout=open("/tmp/log.txt", "w")): # 这些打印语句将转到 /tmp/log.txt print "Test entry 1" print "Test entry 2" # 回到normal stdout print "Back to normal stdout again" 最后,另一个创建临时文件夹并在离开上下文时清理它的示例: from tempfile import mkdtemp from shutil import rmtree @contextmanager deftemporary_dir(*args, **kwds): name = mkdtemp(*args, **kwds) try: yield name finally: shutil.rmtree(name) with temporary_dir() as dirname: # 做任何你想做的事


感谢您将比较添加到 RAII。作为一名 C++ 程序员,他告诉了我我需要知道的一切。
好的,让我弄清楚这一点。您是说 with 语句旨在用数据填充变量,直到其下的指令完成,然后释放变量?
因为我用它打开了一个py脚本。 with open('myScript.py', 'r') as f: pass。我希望能够调用变量 f 来查看文档的文本内容,因为如果通过常规 open 语句将文档分配给 f 会出现这种情况:f = open('myScript.py').read()。但相反,我得到了以下信息:<_io.TextIOWrapper name='myScript.py' mode='r' encoding='cp1252'>。这是什么意思?
@Musixauce3000 - 使用 with 不会消除对 read 实际文件的需要。 with 调用 open - 它不知道您需要用它做什么 - 例如您可能想要进行搜索。
@Musixauce3000 with 语句可以用数据填充变量或对环境进行一些其他更改,直到它下的指令完成,然后执行任何需要的清理。可以完成的清理类型包括关闭打开的文件,或者像 @Tamas 在本例中所做的那样,将目录更改回之前的位置等。由于 Python 具有垃圾收集功能,因此释放变量并不重要用例。 with 通常用于其他类型的清理。
l
legoscia

我会推荐两个有趣的讲座:

PEP 343 “与”声明

Effbot 理解 Python 的“with”语句

1. with 语句用于使用上下文管理器定义的方法包装块的执行。这允许封装常见的 try...except...finally 使用模式以方便重用。

2. 你可以这样做:

with open("foo.txt") as foo_file:
    data = foo_file.read()

或者

from contextlib import nested
with nested(A(), B(), C()) as (X, Y, Z):
   do_something()

或(Python 3.1)

with open('data') as input_file, open('result', 'w') as output_file:
   for line in input_file:
     output_file.write(parse(line))

或者

lock = threading.Lock()
with lock:
    # Critical section of code

3. 我在这里看不到任何反模式。
引用 Dive into Python

试试……终于好了。有更好的。

4. 我想这与程序员使用其他语言的try..catch..finally语句的习惯有关。


当您处理线程同步对象时,它确实发挥了作用。在 Python 中比较少见,但是当你需要它们时,你真的需要 with
diveintopython.org 已关闭(永久?)。镜像于 diveintopython.net
一个好的答案的例子,打开文件是一个很好的例子,它显示了打开、io、关闭文件操作的幕后使用自定义参考名称完全隐藏
文档中给出了在处理文件对象时使用 with 关键字的另一个小示例:docs.python.org/3/tutorial/…
J
Jesper

Python with 语句是 C++ 中常用的 Resource Acquisition Is Initialization 习语的内置语言支持。它旨在允许安全地获取和释放操作系统资源。

with 语句在范围/块内创建资源。您使用块中的资源编写代码。当块退出时,无论块中代码的结果如何(即块正常退出还是由于异常),资源都会被干净地释放。

Python 库中的许多资源都遵循 with 语句所需的协议,因此可以开箱即用地使用它。但是,任何人都可以通过实现有据可查的协议来制作可在 with 语句中使用的资源:PEP 0343

每当您在应用程序中获取必须明确放弃的资源时使用它,例如文件、网络连接、锁等。


J
JudoWill

再次为了完整起见,我将为 with 语句添加我最有用的用例。

我做了很多科学计算,对于某些活动,我需要 Decimal 库来进行任意精度计算。我的代码的某些部分需要高精度,而对于大多数其他部分,我需要的精度较低。

我将默认精度设置为较低的数字,然后使用 with 为某些部分获得更精确的答案:

from decimal import localcontext

with localcontext() as ctx:
    ctx.prec = 42   # Perform a high precision calculation
    s = calculate_something()
s = +s  # Round the final result back to the default precision

我在超几何测试中经常使用它,这需要对大量数进行除法,从而产生形状因子。当您进行基因组规模计算时,您必须小心舍入和溢出错误。


J
John La Rooy

反模式的一个示例可能是在循环内使用 with,而将 with 放在循环外会更有效

例如

for row in lines:
    with open("outfile","a") as f:
        f.write(row)

对比

with open("outfile","a") as f:
    for row in lines:
        f.write(row)

第一种方法是为每个 row 打开和关闭文件,与仅打开和关闭文件一次的第二种方法相比,这可能会导致性能问题。


s
stefanB

请参阅 PEP 343 - The 'with' statement,末尾有一个示例部分。

... Python 语言中的新语句“with”,可以将 try/finally 语句的标准用法分解出来。


c
cobbal

第 1 点、第 2 点和第 3 点得到了很好的覆盖:

4:比较新,只在python2.6+(或者python2.5使用from __future__ import with_statement)有


z
zefciu

with 语句适用于所谓的上下文管理器:

http://docs.python.org/release/2.5.2/lib/typecontextmanager.html

这个想法是通过在离开“with”块后进行必要的清理来简化异常处理。一些 python 内置插件已经用作上下文管理器。


b
bgse

开箱即用支持的另一个示例是流行数据库模块的 connection 对象,如果您习惯了内置 open() 的行为方式,一开始可能会有些困惑,例如:

sqlite3

心理咨询师2

cx_oracle

connection 对象是上下文管理器,因此可以在 with-statement 中直接使用,但是在使用上述内容时请注意:

当 with 块完成时,无论有没有异常,连接都不会关闭。如果 with-block 以异常结束,则回滚事务,否则提交事务。

这意味着程序员必须注意自己关闭连接,但允许获取连接,并在多个 with-statements 中使用它,如 psycopg2 docs 所示:

conn = psycopg2.connect(DSN)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL1)

with conn:
    with conn.cursor() as curs:
        curs.execute(SQL2)

conn.close()

在上面的示例中,您会注意到 psycopg2cursor 对象也是上下文管理器。从有关行为的相关文档中:

当游标退出 with 块时,它会关闭,释放最终与其关联的任何资源。事务的状态不受影响。


C
CFV

在 python 中,“with”语句通常用于打开文件、处理文件中存在的数据,以及在不调用 close() 方法的情况下关闭文件。 “with”语句通过提供清理活动使异常处理更简单。

with的一般形式:

with open(“file name”, “mode”) as file_var:
    processing statements

注意: 无需通过在 file_var.close() 上调用 close() 来关闭文件


T
Timo Huovinen

这里的答案很棒,但只是添加一个对我有帮助的简单答案:

with open("foo.txt") as file:
    data = file.read()

打开返回一个文件

从 2.6 开始,python 将方法 __enter__ 和 __exit__ 添加到文件中。

with 就像一个调用 __enter__ 的 for 循环,运行一次循环然后调用 __exit__

with 适用于任何具有 __enter__ 和 __exit__ 的实例

文件被锁定并且在关闭之前不能被其他进程重用,__exit__ 将其关闭。

来源:http://web.archive.org/web/20180310054708/http://effbot.org/zone/python-with-statement.htm