Как принять и имена файлов и файловые объекты в функциях Python? - PullRequest
20 голосов
/ 01 сентября 2011

В моем коде у меня есть функция 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 ]

8 голосов
/ 27 октября 2014

Одним из способов использования в качестве аргумента имени файла или файлового объекта является реализация диспетчера контекста , который может обрабатывать оба. Реализацию можно найти здесь , я цитирую ради отдельного ответа:

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
7 голосов
/ 01 сентября 2011

Не принимать как файлы, так и строки.Если вы собираетесь принимать файловые объекты, это означает, что вы не будете проверять тип, просто вызовите необходимые методы для фактического параметра (read, write и т. Д.).Если вы собираетесь принимать строки, то в итоге вы получите open -ing-файлы, что означает, что вы не сможете высмеивать параметры.Поэтому я бы сказал, принимать файлы, разрешать вызывающей стороне передавать вам файлоподобный объект и не проверять тип.

2 голосов
/ 07 марта 2019

Я использую упаковщик менеджера контекста.Когда это имя файла (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

Затем вы можете использовать его так:

2 голосов
/ 01 сентября 2011

Python следует за уткой, вы можете проверить, что это объект файла по функции, которая вам нужна от объекта.Например hasattr(obj, 'read') против isinstance(inputelement, file).Для преобразования строки в файловые объекты вы также можете использовать такую ​​конструкцию:

if not hasattr(obj, 'read'):
    obj = StringIO(str(obj))

После этого кода вы сможете безопасно использовать obj в качестве файла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...