"与",上下文管理器,python:简单的术语是什么?

时间:2016-08-15 16:38:38

标签: python with-statement

新手Python编码器来自Java背景。我仍然对此感到困惑:

with open(...) as f:
    do_something(f)
即使在谷歌搜索并在这里阅读了一些答案(我也无法理解他们)。

我的理解是,有一种称为上下文管理器的东西是某种包装器,它包含对创建的文件的引用。关于

as f:

' as'以上就像' as'以下

import numpy as np

它只是一个别名。 ' F'不是指文件,而是指上下文管理器。使用装饰器模式的上下文管理器实现了打开的文件所做的所有方法,这样我就可以像处理文件对象一样对待它(并通过调用相应的方法来获取文件对象,这些方法将在文件上调用在上下文管理器内)。当然,当块完成时,文件将被关闭(整个过程)。

这引出了一个问题:open()通常会返回文件(或对文件的引用)还是上下文管理器?它是否会返回上下文管理器,这是我们在不知情的情况下一直使用的内容?或者它返回文件类型,除非在这个特殊的上下文中返回不同的上下文管理器。

这是否接近正确?有人想澄清吗?

2 个答案:

答案 0 :(得分:3)

文件对象本身就是上下文管理器,因为它们具有__enter____exit__方法。输入和退出上下文时with通知file对象(分别通过调用__enter____exit__),这就是文件对象“知道”关闭的方式文件。这里没有涉及包装器对象;文件对象提供了这两种方法(用Java术语,你可以说文件对象实现了上下文管理器接口)。

请注意,as 是一个别名,就像import module as altname一样;相反,contextmanager.__enter__()返回值被分配给目标。 fileobject.__enter__()方法返回self(因此文件对象本身),以便更容易使用语法:

with open(...) as fileobj:

如果fileobject.__enter__()未执行此操作但返回None或其他对象,则无法内联open()来电;要保留对返回的文件对象的引用,您必须先将open()的结果分配给变量,然后再将其用作上下文管理器:

fileobj = open(...)
with fileobj as something_enter_returned:
    fileobj.write()

fileobj = open(...)
with fileobj:  # no as, ignore whatever fileobj.__enter__() produced 
    fileobj.write()

请注意,没有什么能阻止您在自己的代码中使用后一种模式;如果您已经有另一个对文件对象的引用,或者根本不需要进一步访问文件对象,那么 在此处使用as target部分。

但是,其他情境管理人员可能会返回不同的内容。某些数据库连接器返回数据库游标:

conn = database.connect(....)
with conn as cursor:
    cursor.execute(...)

并退出上下文会导致事务被提交或回滚(取决于是否存在异常)。

答案 1 :(得分:3)

这是您可以创建的最基本的上下文管理器:

class UselessContextManager(object):
    def __enter__(self):
        pass

    def __exit__(self, type, value, traceback):
        pass

with UselessContextManager() as nothing:
    print(nothing is None)

如果您想了解实际流程的外观,请尝试以下方法:

class PrintingContextManager(object):                                          
    def __init__(self, *args, **kwargs):                                       
        print('Initializing with args: {} and kwargs: {}'.format(args, kwargs))

    def __enter__(self):                                                       
        print('I am entering the context')                                     
        print('I am returning 42')                                             
        return 42                                                              

    def __exit__(self, type, value, traceback):                                
        print('And now I am exiting')                                          


print('Creating manager')                                                      
manager = PrintingContextManager()                                             
print('Entering with block')                                                   
with manager as fnord:                                                         
    print('Fnord is {}'.format(fnord))                                         
    print('End of context')                                                    
print('Out of context')                                                        

输出:

Creating manager
Initializing with args: () and kwargs: {}
Entering with block
I am entering the context
I am returning 42
Fnord is 42
End of context
And now I am exiting
Out of context

您应该尝试修改代码以打印type, value, traceback,然后在with块中引发异常。

如您所见,with语法几乎只是简称:

thing = ContextManager()
try:
    stuff = thing.__enter__()
except Exception as e:
    stuff.__exit__(type(e), e.args[0], e.__traceback__)

Though truthfully it's a bit different

您可以看到文件始终是上下文管理器:

>>> f = open('/tmp/spanish_inquisition.txt', 'w')
>>> f.__enter__
<function TextIOWrapper.__enter__>
>>> f.__exit__
<function TextIOWrapper.__exit__>
  

我不知道File可以是一个ContextManager,只需实现两个方法,而无需继承超类或显式实现接口。同样,我对这种语言不熟悉。

在Python中, 显式实现了一个接口。在Java中,您必须指定要遵循的接口。在Python中,你就是这样做的。需要一个类似文件的对象?添加.read()方法。可能.seek().open().close()取决于他们的期望。但在Python中......

it = DecoyDuck()
if it.walks_like_a_duck() and it.talks_like_a_duck() and it.quacks_like_a_duck():
    print('It must be a duck')