如何解决打开一个上下文管理器,但仅在调用方未提供的情况下 从概念上讲,这就是我想要的,但是它按预期失败:我的解决方法-使用contextlib模块有更好的方法吗?
这是一个人为的例子,我的实际用例是(主要是只读的)数据库连接-如果未提供连接,则打开一个。
我已将其更改为with open(xxx) as fo
提供的打开文件句柄。我的想法是,我有一些函数可能需要做一些工作-如果他们的调用者已经给他们提供了一个上下文管理的对象(在这种情况下是文件句柄),请使用它。否则,创建本地上下文管理器。
从概念上讲,这就是我想要的,但是它按预期失败:
def write_it(name,fo=None):
if not fo:
with open(name,"w") as fo:
#I'd want to keep `fo` but it wont work
pass
#assume this is a lot of complex code
#if fo was opened here,it will have been closed already
fo.write(name)
name = "works_w_context.txt"
with open(name,"w") as fo:
write_it(name,fo)
fo.write("\n and it still should be open")
#this fails,as expected
name = "error_wo_context.txt"
write_it(name)
Traceback (most recent call last):
File "test_182_context.py",line 21,in <module>
write_it(name)
File "test_182_context.py",line 12,in write_it
fo.write(data)
ValueError: I/O operation on closed file.
(venv38) myuser@test_182_context$ dir *.txt
-rw-r--r-- 1 myuser staff 52 Oct 1 17:26 works_w_context.txt
-rw-r--r-- 1 myuser staff 0 Oct 1 17:26 error_wo_context.txt
我的解决方法-使用contextlib
模块有更好的方法吗?
我发现这样做的唯一方法是创建一个公共存根函数,该函数可以在需要时打开文件,然后使用文件句柄调用实际的实函数。
但是正如您所看到的,我现在已经重复了4次函数签名。当然,*args
和**kwargs
可以有所帮助,但它们也使代码难以遵循。
def _write_it(name,fo=None):
#assume this is a lot of complex code
data = "some very complicated calculations taking many lines"
fo.write(data)
def write_it(name,"w") as fo:
_write_it(name,fo)
else:
_write_it(name,fo)
name = "works2_w_context.txt"
with open(name,as expected
name = "works2_wo_context.txt"
write_it(name)
是的,它可以正常工作:
(venv38) myuser@test_182_context$ py test_182_context_2.py
(venv38) myuser@test_182_context$ dir *.txt
-rw-r--r-- 1 myuser staff 52 Oct 1 17:29 works2_w_context.txt
-rw-r--r-- 1 myuser staff 52 Oct 1 17:29 works2_wo_context.txt
今天早些时候有人问了一个问题,并提到了contextlib.nullcontext
返回上下文管理器,该上下文管理器从 enter 返回enter_result,否则不执行任何操作。它旨在用作可选上下文管理器的替代。
看起来 就像它与我所追求的东西有关,但同时看起来并不能解决嵌套的核心问题。
解决方法
您可以使用退出堆栈为您自己打开的文件有条件地添加新的上下文管理器。
from contextlib import ExitStack
def write_it(name,fo=None):
with ExitStack() as es:
if fo is None:
fo = es.enter_context(open(name,"w"))
data = ...
fo.write()
如果退出堆栈为空,则with
语句退出时无需执行任何操作。 with
语句完成后,将仅将新打开的文件添加到堆栈中。
如果fo
不是None
,也可以使用空上下文管理器:
from contextlib import nullcontext
def write_it(name,fo=None):
if fo is None:
# With no file received,open a new one and use it
# as the context manager
cm = open(name,"w")
else:
# Create a do-nothing context manager that won't close
# the received file.
cm = nullcontext(fo)
# Get an open file handle from the defined context manager
with cm as f:
data = ...
f.write(data)
空上下文管理器的__enter__
方法返回包装的对象,但其__exit__
方法不执行任何操作。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。