如何在Python函数中接受文件名和类文件对象?

时间:2011-09-01 09:21:10

标签: python file-io

在我的代码中,我有一个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)方法是什么?

4 个答案:

答案 0 :(得分:8)

不接受文件和字符串。如果您要接受类文件对象,则表示您不会检查类型,只需在实际参数(readwrite等上调用所需方法)。如果您要接受字符串,那么您将结束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