这是我认为必须经常提出的事情,但我一直无法找到一个好的解决方案。假设我有一个函数可以传递一个开放资源作为参数(如文件或数据库连接对象)或需要自己创建一个。如果函数需要打开自己的文件,最好的做法通常被认为是这样的:Pythonic方式有条件地使用上下文管理器
with open(myfile) as fh:
# do stuff with open file handle...
以确保文件时with
退出块总是关闭。但是,如果在函数中传递一个现有的文件句柄,它可能本身不应该关闭它。
请考虑以下函数,它可以使用打开的文件对象或将文件路径作为参数的字符串。如果它通过一个文件路径,它应该可以像上面那样编写。否则with
声明应该被省略。这导致重复代码:
def foo(f):
if isinstance(f, basestring):
# Path to file, need to open
with open(f) as fh:
# do stuff with fh...
else:
# Assume open file
fh = f
# do the same stuff...
这当然可以通过定义一个辅助函数,把它在这两个地方是可以避免的,但是这看起来不太优雅。一个更好的办法,我认为是定义一个包装就像一个对象,上下文管理类:
class ContextWrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __enter__(self):
return self.wrapped
def __exit__(self, *args):
pass
def foo(f):
if isinstance(f, basestring):
cm = open(f)
else:
cm = ContextWrapper(f)
with cm as fh:
# do stuff with fh...
这工作,但除非有一个内置的对象,它这个(我不认为这是)我要么随处复制粘贴该对象,要么必须导入我的自定义实用程序模块。我觉得有一个更简单的方法可以做到这一点,我错过了。
我想不出一个很好的理由来编写代码,将接受* *任意路径*或*打开文件句柄。在这种边缘情况下,我建议编写自己的包装(正如你所做的那样) –
我认为辅助函数可能更优雅。你在'foo'中做的很多工作是将参数放到正确类型的对象中 - 一个打开的文件句柄。将执行核心工作的代码分解为一个假定打开文件句柄的助手,我认为总体结果更清晰。实际上,“助手”本身可能是一个合法的公共函数,“foo_from_path”在打开文件处理程序后只是简单地调用“foo”。 – jme
@AdamSmith对于它的价值,这是一种常见的设计,比如numpy。例如,'np.load'采用[string或者fileobj](http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.load.html)。我想这只是为了让一些代码更加简洁,而且因为numpy经常被交互使用,所以这不是不明智的做法。 – jme