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

Ein*_*nar 20 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
Run Code Online (Sandbox Code Playgroud)

或者,两个不同的论点:

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
Run Code Online (Sandbox Code Playgroud)

然而,这两种解决方案都没有让我太过信服,尤其是第二种解决方案,因为我看到了太多的陷阱.

什么是最简洁(和最Pythonic)的方式接受文件类对象或字符串到文本读取功能?

Ros*_*ews 8

不要接受文件和字符串.如果你打算接受类文件对象,那么就意味着你将不会检查类型,只需调用实际参数(所需的方法read,write等等).如果您要接受字符串,那么您将结束open文件,这意味着您将无法模拟参数.所以我会说接受文件,让调用者传递一个类文件对象,不要检查类型.

  • @Niklas:我的意思是说rm不能说服我不要接受文件和字符串作为参数。如果您不使用字符串,则还需要其他一些代码段来打开文件,并且无法模拟参数这一点将适用于该代码段。 (2认同)

Per*_*ses 8

将文件名或类文件对象作为参数的一种方法是实现可以处理两者的上下文管理器.可以在这里找到一个实现,我引用一个自包含的答案:

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
Run Code Online (Sandbox Code Playgroud)

可能的用法:

def load_dataset(file_):
    with open_filename(file_, "r") as f:
        # process here, read only if the file_ is a string
Run Code Online (Sandbox Code Playgroud)

  • 我真的希望这是一个更积极的问题 - 这似乎是解决这个问题的可靠方法,但是缺乏评论和赞成票让我担心有我没有看到的边缘情况,而且我没有处理自定义上下文管理人员之前,所以我不相信我缺乏观察是代码可靠性的证据。 (2认同)

Tie*_*eng 6

我正在使用上下文管理器包装器。当它是文件名 (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
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

with fopen(filename_or_fileobj) as f:
    # do sth. with f
Run Code Online (Sandbox Code Playgroud)