小编典典

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

all

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

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

阅读 84

收藏
2022-03-14

共1个答案

小编典典

  1. 我相信这在我之前已经被其他用户回答过,所以我只是为了完整起见才添加它:该with语句通过将常见的准备和清理任务封装在所谓的上下文管理器中来简化异常处理。更多细节可以在PEP 343中找到。例如,该open语句本身就是一个上下文管理器,它允许您打开一个文件,只要执行是在with您使用它的语句的上下文中就保持打开状态,并在您离开上下文后立即关闭它,无论您是因为异常还是在常规控制流期间离开它。因此,该with语句可以以类似于 C++ 中的RAII 模式的方式使用:某些资源由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
2022-03-14