手动打开上下文管理器

manually open context manager

我的问题是,如何在不使用 with 的情况下执行任何上下文管理器?


Python有上下文管理器的想法,

而不是

file = open('some_file', 'w')
try:
    file.write('Hola!')
finally:
    file.close()
# end try

你可以写

with open('some_file', 'w') as opened_file:
    opened_file.write('Hola!')
# end with

虽然在大多数情况下,第二个是黄金解决方案,但为了在交互式控制台中进行测试和探索,第一个可以更好地使用,因为您可以逐行编写它。

>>> file = open('some_file', 'w')
>>> file.write('Hola!')
>>> file.close()

我的问题是,我怎样才能像这样执行最适合探索的任何 with 上下文管理器?


我的实际用例如下,但请尝试给出一个通用的答案,并且也适用于其他上下文管理器。

import flask

app = flask.Flask(__name__)

with app.test_request_context('/?name=Peter'):
    assert flask.request.path == '/'
    assert flask.request.args['name'] == 'Peter'

from flask docs

你可以调用app.test_request.context('/?name=Peter')到一个变量(例如ctx),然后在它上面调用ctx.__enter__()进入上下文管理器,ctx.__exit__(None, None, None)执行清理。请注意,您将失去上下文管理器的安全保证,除非您将 ctx.__exit__ 放在 finally 子句中。

您仍然可以在交互式控制台中使用 with 语法,但是上下文基于 2 个魔术方法 __enter____exit__,因此您可以只使用它们:

class MyCtx(object):
  def __init__(self, f):
    self.f = f

  def __enter__(self):
    print("Enter")
    return self.f

  def __exit__(*args, **kwargs):
    print("Exit")

def foo():
  print("Hello")

通常你会这样做:

with MyCtx(foo) as f:
  f()

同于:

ctx = MyCtx(foo)
f = ctx.__enter__()
f() 
ctx.__exit__()

这里有 live example

记住上下文 __exit__ 方法用于管理上下文中的错误,因此它们中的大多数都有 __exit__(exception_type, exception_value, traceback) 的签名,如果您不需要为测试处理它,只需给它有一些 None 值:

__exit__(None, None, None)