Проверьте, является ли объект файловым в Python

File-подобные объекты - это объекты в Python, которые ведут себя как настоящий файл, например. имеют метод read() и write(), но имеют другую реализацию. Это и реализация концепции StringIO или объект Socket может использоваться вместо реального файла. Так что плохо выполнить такую ​​проверку:

if not isinstance(fp, file):
   raise something

Каков наилучший способ проверить, является ли объект (например, параметр метода) "файлоподобным"?

Ответ 1

Как правило, не очень хорошая практика иметь такие проверки в вашем коде вообще, если у вас нет особых требований.

В Python типизация является динамической, почему вам нужно проверить, является ли объект похожим на файл, а не просто использовать его, как если бы он был файлом и обрабатывал результирующую ошибку?

Любая проверка, которую вы можете сделать, будет выполняться во время выполнения в любом случае, поэтому выполнение чего-то вроде if not hasattr(fp, 'read') и получение некоторого исключения предоставляет немного больше полезности, чем просто вызов fp.read() и обработка полученной ошибки атрибута, если метод не существует.

Ответ 2

Как говорили другие, вы должны вообще избегать таких проверок. Одним из исключений является то, что объект может быть законным образом иметь разные типы, и вы хотите различное поведение в зависимости от типа. Метод EAFP не всегда работает здесь, так как объект может выглядеть больше, чем один тип утки!

Например, инициализатор может взять файл, строку или экземпляр своего собственного класса. Тогда у вас может быть код:

class A(object):
    def __init__(self, f):
        if isinstance(f, A):
            # Just make a copy.
        elif isinstance(f, file):
            # initialise from the file
        else:
            # treat f as a string

Использование EAFP здесь может вызвать всевозможные тонкие проблемы, так как каждый путь инициализации частично запускается перед выбросом исключения. По сути, эта конструкция имитирует перегрузку функций и, следовательно, не очень Pythonic, но может быть полезна при использовании с осторожностью.

В качестве побочной заметки вы не можете выполнить проверку файла таким же образом в Python 3. Вместо этого вам понадобится что-то вроде isinstance(f, io.IOBase).

Ответ 3

Доминирующей парадигмой здесь является EAFP: проще просить прощения, чем разрешения. Идем дальше и используем интерфейс файла, затем обрабатываем результирующее исключение или позволяем им распространяться на вызывающего.

Ответ 4

Для 3.1+ одно из следующих:

isinstance(something, io.TextIOBase)
isinstance(something, io.BufferedIOBase)
isinstance(something, io.RawIOBase)
isinstance(something, io.IOBase)

Для 2.x "файл-подобный объект" слишком неопределенен для проверки, но документация по любой функции, с которой вы имеете дело, с надеждой скажет вам, что им действительно нужно; если нет, прочитайте код.


Как указывают другие ответы, первое, что нужно задать, - это то, что именно вы проверяете. Обычно EAFP является достаточным и более идиоматичным.

В глоссарии говорится, что "файл-подобный объект" является синонимом "файлового объекта", что в конечном итоге означает его экземпляр одного из трех абстрактные базовые классы, определенные в модуле io, которые сами являются всеми подклассами IOBase. Таким образом, способ проверки точно такой, как показано выше.

(Тем не менее, проверка IOBase не очень полезна. Можете ли вы представить себе случай, когда вам нужно отличить фактический файлоподобный read(size) от некоторой функции с одним аргументом с именем read, которая не является файловым, например, "это объект текстового файла", а не "файл-подобный объект" .)

>

Для 2.x, в то время как модуль io существует с 2.6+, встроенные объекты файла не являются экземплярами классов io, ни один из файловых объектов в stdlib, и ни один из них не является большинство сторонних файловых объектов, с которыми вы, вероятно, столкнетесь. Не было официального определения того, что означает "файл-подобный объект" ; это просто "нечто вроде встроенного файла-объекта", а разные функции означают разные вещи "как". Такие функции должны документировать, что они означают; если они этого не делают, вам нужно посмотреть на код.

Однако наиболее распространенными значениями являются "имеет read(size)", "имеет read()" или "является итерируемым из строк", но некоторые старые библиотеки могут ожидать readline вместо одного из них, некоторые библиотеки как и файлы close(), которые вы им даете, некоторые будут ожидать, что если присутствует fileno, тогда доступны другие функции и т.д. И аналогично для write(buf) (хотя в этом направлении есть намного меньше вариантов).

Ответ 5

Часто бывает полезно сделать ошибку, проверив условие, когда эта ошибка обычно не будет повышаться до намного позже. Это особенно верно для границы между кодом "user-land" и "api".

Вы не поместили бы металлоискатель в полицейский участок на выходе, вы бы поставили его у входа! Если не проверять условие, может возникнуть ошибка, которая могла быть поймана на 100 строк раньше или в суперклассе вместо того, чтобы быть поднятой в подклассе, тогда я говорю, что нет ничего плохого в проверке.

Проверка правильных типов также имеет смысл, если вы принимаете несколько типов. Лучше создать исключение, в котором говорится: "Мне нужен подкласс basestring, OR file", а не просто повышение исключения, потому что у какой-то переменной нет метода "искать"...

Это не означает, что вы сходите с ума и делаете это повсюду, по большей части я согласен с концепцией исключений, которые возникают сами, но если вы можете сделать свой API ясным или избежать ненужного выполнения кода, потому что простое условие имеет не были выполнены!

Ответ 6

Вы можете попробовать и вызвать метод, а затем поймать исключение:

try:
    fp.read()
except AttributeError:
    raise something

Если вам нужен только метод чтения и записи, вы можете сделать это:

if not (hasattr(fp, 'read') and hasattr(fp, 'write')):
   raise something

Если бы я был вами, я бы пошел с методом try/except.

Ответ 7

В большинстве случаев лучший способ справиться с этим - это не делать. Если метод принимает файл-подобный объект, и оказывается, что переданный им объект не является, исключение, возникающее при попытке метода использовать объект, не менее информативно, чем любое исключение, которое вы могли бы явно выражать.

Здесь, по крайней мере, один случай, когда вы, возможно, захотите сделать этот вид проверки, и что, когда объект не будет немедленно использоваться тем, через который вы его передали, например, если он задан в конструкторе класса. В этом случае я бы подумал, что принцип ЕАСП превзойден принципом "не быстро". Я бы проверил объект, чтобы убедиться, что он реализовал методы, которые мой класс нуждается (и что они являются методами), например:

class C():
    def __init__(self, file):
        if type(getattr(file, 'read')) != type(self.__init__):
            raise AttributeError
        self.file = file

Ответ 8

Я столкнулся с вашим вопросом, когда писал функцию open, которая могла принимать имя файла, файловый дескриптор или предварительно открытый файл-подобный объект.

Вместо того, чтобы тестировать метод read, как показывают другие ответы, я закончил проверку, можно ли открыть объект. Если это возможно, это строка или дескриптор, и у меня есть действительный файл-подобный объект в руке из результата. Если open вызывает a TypeError, то объект уже является файлом.