在我的代码中,我有一个load_dataset
函数,它读取文本文件并进行一些处理。最近我考虑过添加对类文件对象的支持,我想知道最好的方法。目前我有两个实现:
首先,输入检查:
if isinstance(inputelement, basestring):
# open file, processing etc
# or
# elif hasattr(inputelement, "read"):
elif isinstance(inputelement, file):
# Do something else
或者,两个不同的论点:
def load_dataset(filename=None, stream=None):
if filename is not None and stream is None:
# open file etc
elif stream is not None and filename is None:
# do something else
然而,这两种解决方案都没有让我相信太多,特别是第二种解决方案,因为我看到了太多的陷阱。
接受类文件对象或字符串到文本读取功能的最干净(也是最Pythonic)方法是什么?
答案 0 :(得分:8)
不接受文件和字符串。如果您要接受类文件对象,则表示您不会检查类型,只需在实际参数(read
,write
等上调用所需方法)。如果您要接受字符串,那么您将结束open
个文件,这意味着您将无法模拟参数。所以我会说接受文件,让调用者传递一个类文件对象,不要检查类型。
答案 1 :(得分:8)
将文件名或类文件对象作为参数的一种方法是实现可以处理两者的context manager。可以找到一个实现here,我引用了一个自包含的答案:
class open_filename(object):
"""Context manager that opens a filename and closes it on exit, but does
nothing for file-like objects.
"""
def __init__(self, filename, *args, **kwargs):
self.closing = kwargs.pop('closing', False)
if isinstance(filename, basestring):
self.fh = open(filename, *args, **kwargs)
self.closing = True
else:
self.fh = filename
def __enter__(self):
return self.fh
def __exit__(self, exc_type, exc_val, exc_tb):
if self.closing:
self.fh.close()
return False
可能的用法:
def load_dataset(file_):
with open_filename(file_, "r") as f:
# process here, read only if the file_ is a string
答案 2 :(得分:2)
Python跟随鸭子打字,您可以通过对象需要的函数来检查这是文件对象。例如hasattr(obj, 'read')
针对isinstance(inputelement, file)
。
要将字符串转换为文件对象,您还可以使用此类构造:
if not hasattr(obj, 'read'):
obj = StringIO(str(obj))
在此代码之后,您将能够安全地将obj
用作文件。
答案 3 :(得分:2)
我正在使用上下文管理器包装器。如果是文件名(str),请在退出时关闭文件。
@contextmanager
def fopen(filein, *args, **kwargs):
if isinstance(filein, str): # filename
with open(filein, *args, **kwargs) as f:
yield f
else: # file-like object
yield filein
然后您可以像使用它一样
with fopen(filename_or_fileobj) as f:
# do sth. with f