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

26 投票
4 回答
5448 浏览
提问于 2025-04-17 01:03

在我的代码中,有一个叫做 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

不过这两种方案都让我不太满意,尤其是第二种,因为我觉得会有很多潜在的问题。

那么,接受一个类似文件的对象或者字符串的函数,最简单(而且最符合Python风格)的做法是什么呢?

4 个回答

6

我在使用一个上下文管理器的包装器。当传入的是文件名(字符串)时,退出时会自动关闭这个文件。

@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
11

一种可以让你传入文件名或者类似文件的对象作为参数的方法,就是实现一个上下文管理器,它能够同时处理这两种情况。你可以在这里找到具体的实现,我引用这段内容是为了提供一个完整的答案:

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
9

不要同时接受文件和字符串。如果你要接受像文件一样的对象,那就意味着你不会检查类型,而是直接调用实际参数需要的方法(比如 readwrite 等)。如果你要接受字符串,那你就得去 open 文件,这样就没办法模拟这些参数。所以我建议你只接受文件,让调用者传给你一个像文件的对象,不用检查类型。

撰写回答