理解 Python 的“with”语句

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32379147/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 11:28:23  来源:igfitidea点击:

Understanding the Python 'with' statement

python

提问by MikeTheReader

I'm trying to understand if there is there a difference between these, and what that difference might be.

我试图了解这些之间是否存在差异,以及这种差异可能是什么。

Option One:

选项一:

file_obj = open('test.txt', 'r')

with file_obj as in_file:
    print in_file.readlines()

Option Two:

选项二:

with open('test.txt', 'r') as in_file:
    print in_file.readlines()

I understand that with Option One, the file_objis in a closed state after the with block.

我知道对于选项一,file_obj在 with 块之后处于关闭状态。

采纳答案by Two-Bit Alchemist

I don't know why no one has mentioned this yet, because it's fundamental to the way withworks. As with many language features in Python, withbehind the scenes calls special methods, which are already defined for built-in Python objects and can be overridden by user-defined classes. In with's particular case (and context managers more generally), the methods are __enter__and __exit__.

我不知道为什么还没有人提到这一点,因为它是工作方式with的基础。与 Python 中的许多语言特性一样,with在幕后调用特殊方法,这些方法已经为内置 Python 对象定义,并且可以被用户定义的类覆盖。在with特殊情况下(以及更一般的上下文管理器),方法是__enter____exit__

Remember that in Python everything is an object-- even literals. This is why you can do things like 'hello'[0]. Thus, it does not matter whether you use the file object directly as returned by open:

请记住,在 Python 中一切都是对象——甚至是文字。这就是为什么你可以做这样的事情'hello'[0]。因此,是否直接使用由 返回的文件对象并不重要open

with open('filename.txt') as infile:
    for line in infile:
        print(line)

or store it first with a different name (for example to break up a long line):

或首先使用不同的名称存储它(例如,拆分长行):

the_file = open('filename' + some_var + '.txt')
with the_file as infile:
    for line in infile:
        print(line)

Because the end result is that the_file, infile, and the return value of openall point to the same object, and that's what withis calling the __enter__and __exit__methods on. The built-in file object's __exit__method is what closes the file.

因为最终结果是the_file, infile, 和open所有的返回值都指向同一个对象,这with就是调用__enter____exit__方法的原因。内置文件对象的__exit__方法是关闭文件的方法。

回答by lvc

These behave identically. As a general rule, the meaning of Python code is not changed by assigning an expression to a variable in the same scope.

这些行为相同。作为一般规则,Python 代码的含义不会通过将表达式分配给同一范围内的变量而改变。

This is the same reason that these are identical:

这与它们相同的原因相同:

f = open("myfile.txt")

vs

对比

filename = "myfile.txt"
f = open(filename)

Regardless of whether you add an alias, the meaning of the code stays the same. The context manager has a deeper meaning than passing an argument to a function, but the principle is the same: the context manager magic is applied to the same object, and the file gets closed in both cases.

无论您是否添加别名,代码的含义都保持不变。上下文管理器比将参数传递给函数具有更深的含义,但原理是相同的:上下文管理器魔法应用于同一个对象,并且在这两种情况下文件都会关闭。

The only reason to choose one over the other is if you feel it helps code clarity or style.

选择一个而不是另一个的唯一原因是,如果您觉得它有助于代码清晰度或风格。

回答by ArtOfWarfare

There is no difference between the two - either way the file is closed when you exit the with block.

两者之间没有区别 - 无论哪种方式,当您退出 with 块时文件都会关闭。

The second example you give is the typical way the files are used in Python 2.6 and newer (when the withsyntax was added).

您给出的第二个示例是在 Python 2.6 和更高版本(with添加语法时)中使用文件的典型方式。

You can verify that the first example also works in a REPL session like this:

您可以验证第一个示例是否也适用于 REPL 会话,如下所示:

>>> file_obj = open('test.txt', 'r')
>>> file_obj.closed
False
>>> with file_obj as in_file:
...     print in_file.readlines()
<Output>
>>> file_obj.closed
True

So after the withblocks exits, the file is closed.

所以在with块退出后,文件被关闭。

Normally the second example is how you would do this sort of thing, though.

不过,通常第二个例子是你将如何做这种事情。

There's no reason to create that extra variable file_obj... anything that you might want to do with it after the end of the withblock you could just use in_filefor, because it's still in scope.

没有理由创建那个额外的变量file_obj......在with块结束后你可能想用它做的任何事情,你都可以使用in_file它,因为它仍在范围内。

>>> in_file
<closed file 'test.txt', mode 'r' at 0x03DC5020>

回答by dawg

If you just fire up Python and use either of those options, the net effect is the same if the base instance of Python's fileobject is not changed. (In Option One, the file is only closed when file_objgoes out of scope vs at the end of the block in Option Two as you have already observed.)

如果你只是启动 Python 并使用这些选项中的任何一个,如果 Pythonfile对象的基本实例没有改变,最终效果是一样。(在选项一中,文件只有在file_obj超出范围时才关闭,而在选项二中的块末尾,正如您已经观察到的那样。)

There canbe differences with use cases with a context managerhowever. Since fileis an object, you can modify it or subclass it.

可能是有差异与使用情况上下文管理不过。由于file是一个对象,您可以修改它或子类化它。

You can also open a file by just calling file(file_name)showing that fileacts like other objects (but no one opens files that way in Python unless it is with with):

你也可以通过调用file(file_name)显示来打开一个文件,file就像其他对象一样(但没有人在 Python 中以这种方式打开文件,除非它是 with with):

>>> f=open('a.txt')
>>> f
<open file 'a.txt', mode 'r' at 0x1064b5ae0>
>>> f.close()

>>> f=file('a.txt')
>>> f
<open file 'a.txt', mode 'r' at 0x1064b5b70>
>>> f.close()


More generally, the opening and closing of some resource called the_thing(commonly a file, but can be anything) you follow these steps:

更一般地,打开和关闭某些称为the_thing(通常是文件,但可以是任何内容)的资源,您可以按照以下步骤操作:

set up the_thing                       # resource specific, open, or call the obj
try                                    # generically __enter__
    yield pieces from the_thing
except
    react if the_thing is broken 
finally, put the_thing away            # generically __exit__

You can more easily change the flow of those subelements using the context manager vs procedural code woven between openand the other elements of the code.

您可以使用上下文管理器与代码open的其他元素之间编织的程序代码更轻松地更改这些子元素的流程。

Since Python 2.5, file objects have __enter__ and __exit__ methods:

从 Python 2.5 开始,文件对象有 __enter__ 和 __exit__ 方法:

>>> f=open('a.txt')
>>> f.__enter__
<built-in method __enter__ of file object at 0x10f836780>
>>> f.__exit__
<built-in method __exit__ of file object at 0x10f836780>

The default Python fileobject uses those methods in this fashion:

默认 Pythonfile对象以这种方式使用这些方法:

__init__(...)            # performs initialization desired

__enter__() -> self      # in the case of `file()` return an open file handle

__exit__(*excinfo) -> None.  # in the case of `file()` closes the file.

These methods can be changed for your own use to modify how a resource is treated when it is opened or closed. A context manager makes it really easyto modify what happens when you open or close a file.

可以更改这些方法供您自己使用,以修改资源在打开或关闭时的处理方式。上下文管理器可以非常轻松地修改打开或关闭文件时发生的情况。

Trivial example:

简单的例子:

class Myopen(object):
    def __init__(self, fn, opening='', closing='', mode='r', buffering=-1):
        # set up the_thing

        if opening:
            print(opening)
        self.closing=closing    
        self.f=open(fn, mode, buffering)

    def __enter__(self):
        # set up the_thing
        # could lock the resource here
        return self.f

    def __exit__(self, exc_type, exc_value, traceback):
        # put the_thing away
        # unlock, or whatever context applicable put away the_thing requires
        self.f.close()
        if self.closing:
            print(self.closing)  

Now try that:

现在试试:

>>> with Myopen('a.txt', opening='Hello', closing='Good Night') as f:
...     print f.read()
...
Hello
[contents of the file 'a.txt']
Good Night

Once you have control of entry and exit to a resource, there are many use cases:

一旦您控制了资源的进入和退出,就会有许多用例:

  1. Lock a resource to access it and use it; unlock when you are done
  2. Make a quirky resource (like a memory file, database or web page) act more like a straight file resource
  3. Open a database and rollback if there is an exception but commit all writes if there are no errors
  4. Temporarily change the context of a floating point calculation
  5. Time a piece of code
  6. Change the exceptions that you raise by returning Trueor Falsefrom the __exit__ method.
  1. 锁定资源以访问和使用它;完成后解锁
  2. 使古怪的资源(如内存文件、数据库或网页)表现得更像直接文件资源
  3. 如果有异常则打开数据库并回滚,但如果没有错误则提交所有写入
  4. 临时更改浮点计算的上下文
  5. 计时一段代码
  6. 更改通过返回TrueFalse从 __exit__ 方法引发的异常。

You can read more examples in PEP 343.

您可以在PEP 343 中阅读更多示例