我的代码:
fh = None
if args.optional_input_file_path is not None:
fh = open(args.optional_input_file_path)
my_function(foo, bar, fh)
if args.optional_input_file_path is not None:
fh.close()
我不喜欢我需要写
if args.optional_input_file is not None:
两次。
在
my_function
内移动条件逻辑也不理想,因为我故意将 IO
对象传递给函数而不是文件路径,以便于测试。
有没有更简洁的方法来实现同样的事情?
我希望能够只写一次
my_function(foo, bar, fh)
,并让fh
是一个IO
对象或None
,这取决于args.optional_input_file_path
的值。
使用自定义 context manager 传播
None
但在其他方面表现得像 open
:
import contextlib
@contextlib.contextmanager
def open_not_none(file, *args, **kwargs):
if file is not None:
with open(file, *args, **kwargs) as f:
yield f
else:
yield None
可以用作
with open_not_none(args.optional_input_file_path, "r") as fh:
my_function(foo, bar, fh)
这使您可以保留在 with 语句 中打开文件的所有好处,同时避免为
my_function
和非None
情况重复调用 None
。如果您愿意,您也可以使用 typing.overload
丰富地键入提示。
试试这个
my_function(foo, bar, open(args.file_path) if args.file_path else None)
因为没有引用打开的文件,它会在下一次GC时自动关闭
fh = None
if args.optional_input_file_path is not None:
fh = open(args.optional_input_file_path)
my_function(foo, bar, fh)
fh = None
# del fh # faster than setting it to None
再次将 fh 设置为 None 应该会触发它的删除,如果文件没有存储在其他地方,这会导致文件被关闭。
如果它存储在其他地方,那么您不再有责任关闭它,这称为 RAII。
contextlib 的文档建议以下内容来处理可选上下文:
def process_file(filename):
if filename is not None:
cm = open(filename)
else:
cm = nullcontext()
with cm as file: # file will be None if no filename
my_function(...., file)
您可以为此目的使用装饰器。
def open_file(nested_function):
def decorated(foo, bar, fh):
fh = open(args.optional_input_file_path)
nested_function(foo, bar, fh)
fh.close()
return decorated
fh = None
if args.optional_input_file_path is not None:
decorated = open_file(my_function)
decorated(foo, bar, fh)
else:
my_function(foo, bar, fh)