总结:
有多种函数,能够传入两种对象非常有用:一种是表示路径的对象(通常是字符串),另一种是表示某种流的对象(通常是从IOBase
派生的对象,但并不总是).这种不同的功能如何区分这两种对象,以便能够适当地处理它们?
假设我有一个函数,用于从某种对象文件生成器方法写入文件:
spiff = MySpiffy()
def spiffy_file_makerA(spiffy_obj, file):
file_str = '\n'.join(spiffy_obj.gen_file())
file.write(file_str)
with open('spiff.out', 'x') as f:
spiffy_file_makerA(spiff, f)
...do other stuff with f...
这很管用.耶.但我宁愿不必担心先打开文件或传递流,至少有时是这样...因此,我重构时能够采用类似文件路径的对象,而不是类似文件的对象,并使用return
语句:
def spiffy_file_makerB(spiffy_obj, file, mode):
file_str = '\n'.join(spiffy_obj.gen_file())
file = open(file, mode)
file.write(file_str)
return file
with spiffy_file_makerB(spiff, 'file.out', 'x') as f:
...do other stuff with f...
但现在我有了一个 idea ,那就是使用第三个函数将其他两个版本结合起来,这取决于file
是类似于文件还是类似于文件路径,但会将类似于f目标文件的对象返回给上下文管理器.这样我就可以编写这样的代码:
with spiffy_file_makerAB(spiffy_obj, file_path_like, mode = 'x') as f:
...do other stuff with f...
...但也像这样:
file_like_obj = get_some_socket_or_stream()
with spiffy_file_makerAB(spiffy_obj, file_like_obj, mode = 'x'):
...do other stuff with file_like_obj...
# file_like_obj stream closes when context manager exits
# unless `closefd=False`
请注意,这将需要与上面提供的简化版本稍有不同的内容.
尽我所能,我还没有找到一个明显的方法来做到这一点,而我找到的方法似乎很做作,只是以后可能会出现问题.例如:
def spiffy_file_makerAB(spiffy_obj, file, mode, *, closefd=True):
try:
# file-like (use the file descriptor to open)
result_f = open(file.fileno(), mode, closefd=closefd)
except TypeError:
# file-path-like
result_f = open(file, mode)
finally:
file_str = '\n'.join(spiffy_obj.gen_file())
result_f.write(file_str)
return result_f
有没有更好的建议?我是否偏离了底线,需要以完全不同的方式处理这件事?