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


Файлоподобные объекты являются объектами в Python, которые ведут себя как реальный файл, например, имеют метод чтения() и метод записи (), но имеют другую реализацию. Это и есть реализация Утиной Типизацией

8 75

8 ответов:

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

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

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

Как уже говорили другие, вы должны вообще избегать таких проверок. Одно исключение - когда объект может быть законно разных типов, и вы хотите различное поведение в зависимости от типа. Метод 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 здесь может вызвать все виды тонкие проблемы, поскольку каждый путь инициализации частично запускается перед созданием исключения. По сути, эта конструкция имитирует функцию утяжеления и так не очень подходящие для Python, но это может быть полезно, если использовать с осторожностью.

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

для 3.1+, одно из следующего:

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

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


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

глоссарий говорит "file-like object "- это синоним" file object", что в конечном итоге означает, что это экземпляр одного из трех абстрактные базовые классы определена в the io модуль, которые сами являются подклассами IOBase. Таким образом, способ проверки точно так же, как показано выше.

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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