首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >"with",上下文管理器,python:简单地说是怎么回事?

"with",上下文管理器,python:简单地说是怎么回事?
EN

Stack Overflow用户
提问于 2016-08-15 16:38:38
回答 2查看 1.7K关注 0票数 1

这里的初学者Python编码器来自Java背景。我仍然对此感到困惑:

代码语言:javascript
复制
with open(...) as f:
    do_something(f)

甚至在谷歌和阅读了这里的一些答案之后(我只是无法理解这些答案)。

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

代码语言:javascript
复制
as f:

上面的“as”就像下面的“as”

代码语言:javascript
复制
import numpy as np

只是个化名。'f‘不是指文件,而是指上下文管理器。上下文管理器使用修饰器模式实现所打开的文件所执行的所有方法,这样我就可以把它当作文件对象来对待(并通过调用适当的方法来获取文件对象,这些方法将在上下文管理器中的文件中调用)。当然,当块完成时,文件将被关闭(这是整个过程的要点)。

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

这附近有吗?有人想澄清吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-08-15 16:41:47

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

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

代码语言:javascript
复制
with open(...) as fileobj:

如果fileobject.__enter__()没有这样做,但返回了None或其他对象,则无法内联open()调用;要保持对返回的文件对象的引用,必须先将open()的结果赋值给变量,然后再将其用作上下文管理器:

代码语言:javascript
复制
fileobj = open(...)
with fileobj as something_enter_returned:
    fileobj.write()

代码语言:javascript
复制
fileobj = open(...)
with fileobj:  # no as, ignore whatever fileobj.__enter__() produced 
    fileobj.write()

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

但是,其他上下文管理器可能返回一些不同的内容。一些数据库连接器返回数据库游标:

代码语言:javascript
复制
conn = database.connect(....)
with conn as cursor:
    cursor.execute(...)

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

票数 4
EN

Stack Overflow用户

发布于 2016-08-15 17:04:59

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

代码语言:javascript
复制
class UselessContextManager(object):
    def __enter__(self):
        pass

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

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

如果您想要对实际流程流程的外观有一点了解,请尝试以下一个:

代码语言:javascript
复制
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')                                                        

输出:

代码语言:javascript
复制
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语法几乎只是以下的缩写:

代码语言:javascript
复制
thing = ContextManager()
try:
    stuff = thing.__enter__()
except Exception as e:
    stuff.__exit__(type(e), e.args[0], e.__traceback__)

但说实话,这有点不一样

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

代码语言:javascript
复制
>>> f = open('/tmp/spanish_inquisition.txt', 'w')
>>> f.__enter__
<function TextIOWrapper.__enter__>
>>> f.__exit__
<function TextIOWrapper.__exit__>

我不知道一个文件可以是一个ContextManager,只需实现两个方法,而不继承超类或显式实现接口。再说一次,我对这门语言很陌生。

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

代码语言:javascript
复制
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')
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38959182

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档