Файловые объекты - это объекты в Python, которые ведут себя как настоящий файл, например, имеют методы чтения () и записи (), но имеют другую реализацию. Это и есть реализация концепции Duck Typing .
Считается хорошей практикой разрешать файловый объект везде, где ожидается файл, чтобы, например, объект StringIO или объект Socket можно было использовать вместо реального файла. Так что делать такую проверку - плохо:
if not isinstance(fp, file):
raise something
Как лучше всего проверить, является ли объект (например, параметр метода) "файловым"?
why
а как насчет операторов вроде__add__
,__lshift__
или__or__
в кастомных классах? (объект файла и API: docs.python.org/glossary.html#term-file-object )Для версии 3.1+ одно из следующих:
Для 2.x «файловый объект» - слишком расплывчатая вещь, чтобы проверить, но документация для любой функции (й), с которой вы имеете дело, надеюсь, скажет вам, что им на самом деле нужно; если нет, прочтите код.
Как указывают другие ответы, первое, что нужно спросить, - это то, что именно вы проверяете. Обычно достаточно EAFP, и это более идиоматично.
Словарь говорит «файл-подобный объект» является синонимом «файлового объекта», который в конечном счете означает , что это экземпляр одного из трех абстрактных базовых классов , определенных в в
io
модуле , которые сами по себе все подклассыIOBase
. Итак, способ проверки точно такой, как показано выше.(Однако проверка
IOBase
не очень полезна. Можете ли вы представить себе случай, когда вам нужно отличить реальный файловыйread(size)
от некоторой функции с одним аргументом,read
которая не является файловой, без необходимости различать текстовые файлы и необработанные бинарные файлы? Итак, действительно, вы почти всегда хотите проверить, например, «является ли объект текстовым файлом», а не «является ли объект подобным файлу».)Для 2.x, хотя
io
модуль существует с версии 2.6+, встроенные файловые объекты не являются экземплярамиio
классов, ни один из файловых объектов в stdlib, а также большинство сторонних файловых объектов, которые вы наверняка встретите. Официального определения того, что означает «файловый объект», не существовало; это просто «что-то вроде встроенного файлового объекта », а разные функции означают разные вещи под «подобным». Такие функции должны документировать, что они означают; если нет, вы должны посмотреть на код.Однако наиболее распространенными значениями являются «имеет
read(size)
», «имеетread()
» или «является итерируемой строкой», но некоторые старые библиотеки могут ожидатьreadline
вместо одной из них, некоторые библиотеки любятclose()
файлы, которые вы им даете, некоторые будут ожидать, что еслиfileno
присутствует, то доступны другие функции и т. д. И аналогично дляwrite(buf)
(хотя в этом направлении вариантов намного меньше).источник
IOBase
. Например, приборы pytest дают вам то,_pytest.capture.EncodedFile
что ни от чего не наследует.Как говорили другие, вам следует избегать таких проверок. Одно исключение - это когда объект может быть разных типов, и вам нужно различное поведение в зависимости от типа. Метод 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)
Вместо этого вам понадобится что-то вроде .источник
Здесь доминирует парадигма EAFP: проще просить прощения, чем разрешения. Продолжайте и используйте файловый интерфейс, а затем обработайте полученное исключение или позвольте им распространиться на вызывающего.
источник
x
не файловый, тоx.read()
вызовет собственное исключение. Зачем писать лишнее if-выражение? Просто используйте объект. Это либо сработает, либо сломается.Часто бывает полезно вызвать ошибку, проверив условие, когда эта ошибка обычно возникает гораздо позже. Это особенно верно для границы между кодом пользователя и api.
Вы бы не разместили металлоискатель в полицейском участке на выходной двери, вы бы разместили его у входа! Если невыполнение проверки условия означает, что может произойти ошибка, которая могла быть обнаружена на 100 строк раньше, или в суперклассе, а не в подклассе, тогда я говорю, что в проверке нет ничего плохого.
Проверка на правильность типов также имеет смысл, если вы принимаете более одного типа. Лучше создать исключение, в котором говорится: «Мне нужен подкласс basestring, OR file», чем просто вызвать исключение, потому что у некоторой переменной нет метода поиска ...
Это не значит, что вы сходите с ума и делаете это везде, по большей части я согласен с концепцией возникновения исключений, но если вы можете сделать свой API радикально понятным или избежать ненужного выполнения кода, потому что простое условие не было выполнено Сделай так!
источник
Вы можете попробовать вызвать метод и перехватить исключение:
try: fp.read() except AttributeError: raise something
Если вам нужен только метод чтения и записи, вы можете сделать это:
if not (hasattr(fp, 'read') and hasattr(fp, 'write')): raise something
На вашем месте я бы выбрал метод try / except.
источник
try
всегда первый выбор. Толькоhasattr
проверки - по какой-то очень непонятной причине - вы не можете просто использоватьtry
.fp.read(0)
вместоfp.read()
, чтобы не помещать весь код вtry
блок, если вы хотитеfp
впоследствии обрабатывать данные .fp.read()
с большими файлами сразу увеличится использование памяти.Flask
я сделал это и понял, что базовыйFileStorage
объект нуждается в сбросе указателя после чтения.В большинстве случаев лучший способ справиться с этим - не делать этого. Если метод принимает объект, подобный файлу, и оказывается, что переданный объект им не является, исключение, которое возникает, когда метод пытается использовать объект, не менее информативно, чем любое исключение, которое вы могли вызвать явно.
Тем не менее, есть по крайней мере один случай, когда вы можете захотеть провести такую проверку, и это когда объект не используется немедленно тем, кому вы его передали, например, если он установлен в конструкторе класса. В этом случае я бы подумал, что принцип EAFP превосходит принцип «быстро отказывать». Я бы проверил объект, чтобы убедиться, что он реализует методы, которые нужны моему классу (и что они методы), например:
class C(): def __init__(self, file): if type(getattr(file, 'read')) != type(self.__init__): raise AttributeError self.file = file
источник
getattr(file, 'read')
вместо того, чтобы простоfile.read
? Это делает то же самое.file
экземпляр. (Методы экземпляров типов встроенных / C-расширений относятся к типуbuiltin_function_or_method
, а методы классов старого стиля - к типуinstancemethod
). Тот факт, что это класс старого стиля и что он использует==
типы вместоininstance
илиissubclass
, является дополнительной проблемой, но если основная идея не работает, это вряд ли имеет значение.Я столкнулся с вашим вопросом, когда писал
open
-подобную функцию, которая могла бы принимать имя файла, дескриптор файла или предварительно открытый объект, подобный файлу.Вместо того, чтобы тестировать
read
метод, как предполагают другие ответы, я в конечном итоге проверил, можно ли открыть объект. Если можно, то это строка или дескриптор, и у меня в руках есть действительный файловый объект из результата. Еслиopen
возникает aTypeError
, значит, объект уже является файлом.источник