理解Python'with'语句

Mik*_*der 27 python

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

方案一:

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

with file_obj as in_file:
    print in_file.readlines()
Run Code Online (Sandbox Code Playgroud)

方案二:

with open('test.txt', 'r') as in_file:
    print in_file.readlines()
Run Code Online (Sandbox Code Playgroud)

我理解,对于Option 1,file_obj在with块之后处于关闭状态.

Two*_*ist 32

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

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

with open('filename.txt') as infile:
    for line in infile:
        print(line)
Run Code Online (Sandbox Code Playgroud)

或者先用不同的名称存储它(例如分解一条长行):

the_file = open('filename' + some_var + '.txt')
with the_file as infile:
    for line in infile:
        print(line)
Run Code Online (Sandbox Code Playgroud)

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

  • 我相信它只是返回文件对象(本身)而不做其他任何事情。[本页(最后一段)](http://effbot.org/zone/python-with-statement.htm) 似乎证实了这一点。 (2认同)
  • 是的,来自`__enter__`的返回值很重要,但如果您使用的唯一上下文管理器是文件,您可能不会注意到它.当你用foo作为bar`时,`bar`可能与`foo`相同或不同.这将是什么`foo .__ enter__`返回,这可能是别的东西. (2认同)

lvc*_*lvc 9

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

这与这些相同的原因相同:

f = open("myfile.txt")
Run Code Online (Sandbox Code Playgroud)

VS

filename = "myfile.txt"
f = open(filename)
Run Code Online (Sandbox Code Playgroud)

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

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

  • @lvc查看问题以及那些还不知道答案的人的初步反应.令人困惑的是文件对象是否会在最后关闭.通过内联调用,每个人都是_sure_它将被关闭,因为这就是`with`的工作方式.使用预先创建的`file_obj`,突然之间每个人都不太确定.但为什么?因为最终它与_that对象的``__exit__`被调用有关.你的例子看起来像是错过了重点.它与传递给open的内容无关,而是与它返回的对象所做的一切有关. (3认同)

Art*_*are 5

两者之间没有区别 - 退出with块时文件关闭的方式.

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

您可以验证第一个示例是否也适用于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
Run Code Online (Sandbox Code Playgroud)

因此,在with块退出后,文件将关闭.

通常,第二个例子是你如何做这种事情.

没有理由创建那个额外的变量file_obj......在with你可以使用的块结束之后你可能想要用它做什么in_file,因为它仍然在范围内.

>>> in_file
<closed file 'test.txt', mode 'r' at 0x03DC5020>
Run Code Online (Sandbox Code Playgroud)


daw*_*awg 5

如果您只是启动Python并使用其中任何一个选项,那么如果file不更改Python 对象的基本实例,则净效果是相同.(在选项一中,文件仅在file_obj超出范围时关闭,而在选项二中的块结尾处才会关闭,如您已经观察到的那样.)

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

您也可以通过调用file(file_name)显示file与其他对象一样的方式来打开文件(但没有人在Python中以这种方式打开文件,除非它与之一起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()
Run Code Online (Sandbox Code Playgroud)

更一般地说,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__
Run Code Online (Sandbox Code Playgroud)

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

从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>
Run Code Online (Sandbox Code Playgroud)

默认的Python file对象以这种方式使用这些方法:

__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.
Run Code Online (Sandbox Code Playgroud)

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

琐碎的例子:

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)  
Run Code Online (Sandbox Code Playgroud)

现在尝试:

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

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

  1. 锁定资源以访问它并使用它; 完成后解锁
  2. 使一个古怪的资源(如内存文件,数据库或网页)更像是一个直接的文件资源
  3. 如果存在异常,则打开数据库并回滚,但如果没有错误则提交所有写入
  4. 暂时更改浮点计算的上下文
  5. 计算一段代码
  6. 通过返回TrueFalse从__exit__方法更改引发的异常.

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