理解Python'with'语句

时间:2015-09-03 15:00:01

标签: python

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

选项一:

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

with file_obj as in_file:
    print in_file.readlines()

选项二:

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

我理解使用选项一,file_obj在with块之后处于关闭状态。

5 个答案:

答案 0 :(得分:30)

我不知道为什么没人提到这一点,因为它是the way with works的基础。与Python中的许多语言功能一样,with幕后调用special methods,它们已经为内置Python对象定义,可以被用户定义的类覆盖。在with的特定情况下(以及更常见的上下文管理器),方法为__enter____exit__

请记住,在Python 一切都是对象 - 甚至是文字。这就是你可以做'hello'[0]之类的事情的原因。因此,您是否直接使用open

返回的文件对象并不重要
with open('filename.txt') as infile:
    for line in infile:
        print(line)

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

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

因为最终结果是the_fileinfileopen的返回值都指向同一个对象,而with是什么正在调用__enter____exit__方法。内置文件对象的__exit__方法是关闭文件的。

答案 1 :(得分:9)

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

这是相同的原因:

f = open("myfile.txt")

VS

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

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

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

答案 2 :(得分: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

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

通常,第二个例子就是你如何做这类事情。

没有理由创建额外的变量file_obj ...在with块结束后你可能想要用它来做任何事情你可以使用{{1因为它仍然在范围内。

in_file

答案 3 :(得分:5)

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

然而 与<{3}}的用例存在差异。由于file是一个对象,您可以对其进行修改或对其进行子类化。

您也可以通过调用file(file_name)来显示file与其他对象一样(但没有人在Python中打开文件,除非它与with一起),从而打开文件:< / p>

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

更一般地说,您按照以下步骤打开和关闭一些名为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__

您可以使用上下文管理器与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>

默认的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.

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

琐碎的例子:

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)  

现在尝试:

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

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

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

    中阅读更多示例

答案 4 :(得分:0)

即使withreturn在内部被调用(也意味着sys.exit()仍被调用),__exit__仍然有效:

#!/usr/bin/env python
import sys

class MyClass:
  def __enter__(self):
    print("Enter") 
    return self

  def __exit__(self, type, value, trace):
    print("type: {} | value: {} | trace: {}".format(type,value,trace))

# main code:
def myfunc(msg):
  with MyClass() as sample:
    print(msg)
    # also works if uncomment this:
    # sys.exit(0) 
    return

myfunc("Hello")

return版本将显示:

Enter
Hello
type: None | value: None | trace: None

exit(0)版本将显示:

Enter
Hello
type: <class 'SystemExit'> | value: 0 | trace: <traceback object at 0x7faca83a7e00>