我遇到了Python with 今天首次发表声明。我已经轻度使用Python几个月了,甚至不知道它的存在!鉴于其有点模糊的地位,我认为值得一问:

  1. 什么是Python with 陈述 设计用于?
  2. 做什么 你用它来做什么?
  3. 有没有 我需要注意的陷阱,或者 与以下项相关的常见反模式 它的用途?任何更好使用的情况 try..finallywith?
  4. 为什么它没有得到更广泛的应用?
  5. 哪些标准库类与其兼容?
有帮助吗?

解决方案

  1. 我相信这个问题已经被我之前的其他用户回答过,所以我只是为了完整起见添加它:这 with 语句通过将常见的准备和清理任务封装在所谓的 上下文管理器. 。更多详细信息可以参见 公众号 343. 。例如, open 语句本身就是一个上下文管理器,它允许您打开一个文件,只要在语句的上下文中执行,就保持文件打开状态 with 声明您使用它的位置,并在离开上下文后立即关闭它,无论您是因为异常还是在常规控制流期间离开它。这 with 因此,语句可以以类似于 RAII模式 在 C++ 中:某些资源是由 with 声明并在您离开时发布 with 语境。

  2. 一些例子是:使用打开文件 with open(filename) as fp:, ,使用获取锁 with lock: (在哪里 lock 是一个实例 threading.Lock)。您还可以使用以下方法构建自己的上下文管理器 contextmanager 装饰器来自 contextlib. 。例如,当我必须暂时更改当前目录然后返回到原来的位置时,我经常使用此方法:

    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 within data/stuff
    # here I am back again in the original working directory
    

    这是临时重定向的另一个示例 sys.stdin, sys.stdoutsys.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)
                if stream is not None and stream != getattr(sys, sname):
                    old_streams[sname] = getattr(sys, sname)
                    setattr(sys, sname, stream)
            yield
        finally:
            for sname, stream in old_streams.iteritems():
                setattr(sys, sname, stream)
    
    with redirected(stdout=open("/tmp/log.txt", "w")):
         # these print statements will go to /tmp/log.txt
         print "Test entry 1"
         print "Test entry 2"
    # back to the normal stdout
    print "Back to normal stdout again"
    

    最后,另一个创建临时文件夹并在离开上下文时清理它的示例:

    from tempfile import mkdtemp
    from shutil import rmtree
    
    @contextmanager
    def temporary_dir(*args, **kwds):
        name = mkdtemp(*args, **kwds)
        try:
            yield name
        finally:
            shutil.rmtree(name)
    
    with temporary_dir() as dirname:
        # do whatever you want
    

其他提示

我推荐两场有趣的讲座:

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.我在这里没有看到任何反模式。
引用 深入Python:

尝试..终于很好。与更好。

4.我估计和程序员的使用习惯有关 try..catch..finally 来自其他语言的声明。

蟒蛇 with 声明是内置语言支持的 Resource Acquisition Is Initialization C++ 中常用的习惯用法。它的目的是允许安全获取和释放操作系统资源。

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

Python库中的许多资源都遵循Python所需的协议 with 语句,因此可以开箱即用。然而,任何人都可以通过实现记录良好的协议来创建可在 with 语句中使用的资源: 公众号 0343

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

反模式的一个例子可能是使用 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 与仅打开和关闭文件一次的第二种方式相比,这可能会导致性能问题。

再次为了完整性,我将添加我最有用的用例 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

我在超几何测试中经常使用它,该测试需要对大量结果进行阶乘除法。当您进行基因组规模计算时,您必须小心舍入和溢出错误。

PEP 343 - 'with' 语句, ,最后有一个示例部分。

...Python 的新语句 “with” 要制作的语言 可以分解 try/finally 语句的标准用法。

第 1、2 和 3 点已相当充分地涵盖:

4: 它相对较新,仅在 python2.6+ 中可用(或 python2.5 使用 from __future__ import with_statement)

with 语句与所谓的上下文管理器一起使用:

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

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

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

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

当。。。的时候 with-block 已完成,无论是否有例外, 连接未关闭. 。如果 with-block 完成时出现异常,则回滚事务,否则提交事务。

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

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()

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

当一个 cursor 退出 with-block 它被关闭,释放最终与其关联的任何资源。事务的状态不受影响。

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

with 的一般形式:

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

笔记: 无需通过在 file-var.close() 上调用 close() 来关闭文件

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top