Предположим, у меня есть функция, которая работает с текстовым файлом - например, читает из него и удаляет слово «а». Я мог бы либо передать ему имя файла и обработать открытие / закрытие в функции, либо я мог бы передать ему открытый файл и ожидать, что тот, кто его вызовет, будет иметь дело с его закрытием.
Первый способ кажется лучшим способом гарантировать, что файлы не остаются открытыми, но он не позволяет мне использовать такие вещи, как объекты StringIO
Второй способ может быть немного опасным - нет способа узнать, будет ли файл закрыт или нет, но я бы мог использовать файловые объекты
def ver_1(filename):
with open(filename, 'r') as f:
return do_stuff(f)
def ver_2(open_file):
return do_stuff(open_file)
print ver_1('my_file.txt')
with open('my_file.txt', 'r') as f:
print ver_2(f)
Является ли один из них вообще предпочтительным? Обычно ожидается, что функция будет вести себя одним из этих двух способов? Или это должно быть хорошо задокументировано, чтобы программист мог использовать эту функцию соответствующим образом?
источник
your_function
этом отношении может использоваться необязательный аргумент «stream_name» .Настоящий вопрос заключается в полноте. Является ли ваша функция обработки файлов полной обработкой файла, или это всего лишь одна часть в цепочке этапов обработки? Если он завершен сам по себе, то не стесняйтесь инкапсулировать весь доступ к файлам внутри функции.
Это имеет очень приятное свойство завершения ресурса (закрытия файла) в конце
with
оператора.Однако если есть необходимость в обработке уже открытого файла, тогда различие между вами
ver_1
иver_2
имеет больший смысл. Например:Этот вид явного тестирования типов часто не одобряется , особенно в таких языках, как Java, Julia и Go, где непосредственная поддержка диспетчеризации на основе типов или интерфейсов поддерживается. В Python, однако, нет языковой поддержки для диспетчеризации на основе типов. Иногда вы можете увидеть критику прямого типового тестирования в Python, но на практике это очень распространенное и довольно эффективное действие. Это позволяет функции иметь высокую степень универсальности, обрабатывая любые типы данных, которые могут появиться на ее пути, то есть «типизацию утки». Обратите внимание на подчеркивание
_ver_file
; это обычный способ обозначения «частной» функции (или метода). Хотя технически это можно назвать напрямую, оно предполагает, что функция не предназначена для прямого внешнего потребления.Обновление 2019: учитывая недавние обновления в Python 3, например, что пути теперь потенциально могут храниться как
pathlib.Path
объекты, а не простоstr
илиbytes
(3.4+), и что подсказка типа перешла от эзотерической к основной (около 3.6+, хотя все еще активно развивается), вот обновленный код, который учитывает эти достижения:источник
read
что-то, похожее на файл, или вызовopen(fileobj, 'r')
и отлавливаниеTypeError
iffileobj
не является строкой.ver
операцию независимо от типа. Как вы могли быver
сказать, это также можно реализовать с помощью утки. Но генерирование тогда отлавливающих исключений происходит медленнее, чем простая проверка типов, и IMO не дает какой-либо конкретной выгоды (ясность, универсальность и т. Д.). По моему опыту, типизация утки замечательна "в целом", но нейтральна для контрпродуктивности "в малых «.hasattr(fileobj, 'read')
Тест будет утиной типизации;isinstance(fileobj, str)
тест не является. Вот пример различия:isinstance
тест завершается неудачно с именами файлов в Юникоде, посколькуu'adsf.txt'
не являетсяstr
. Вы проверили на слишком конкретный тип. Тест на утку, основанный на вызовеopen
или некоторой гипотетическойdoes_this_object_represent_a_filename
функции, не будет иметь такой проблемы.is_instance(x, str)
а скорее что-то вродеis_instance(x, string_types)
, сstring_types
должным образом настроенным для правильной работы в PY2 и PY3. Учитывая что-то, что крякает как строка,ver
будет правильно реагировать; дано то, что крякает как файл, то же самое. К пользователю оver
, не было бы никакой разницы - за исключением того, что осуществление инспекции типа будет работать быстрее. Пуристы утки: не стесняйтесь не соглашаться.Если вы передаете имя файла вместо дескриптора файла, то нет никакой гарантии, что второй файл при открытии совпадает с первым; это может привести к исправлению ошибок и брешей в безопасности.
источник
Это касается владения и ответственности за закрытие файла. Вы можете передать дескриптор потока или файла или любую другую вещь, которая должна быть закрыта / удалена в какой-то момент, другому методу, при условии, что вы уверены, что ясно, кто ей владеет, и уверены, что она будет закрыта владельцем, когда вы закончите. , Как правило, это включает в себя конструкцию try-finally или одноразовый шаблон.
источник
Если вы решите передать открытые файлы, вы можете сделать что-то вроде следующего, НО у вас нет доступа к имени файла в функции, которая записывает в файл.
Я сделал бы это, если бы хотел иметь класс, который на 100% отвечал бы за операции с файлами / потоками, а также другие классы или функции, которые были бы наивными и не предполагали, что они будут открывать или закрывать указанные файлы / потоки.
Помните, что менеджеры контекста работают так, как будто в них есть предложение finally. Таким образом, если в функцию записи выдается исключение, файл будет закрыт, несмотря ни на что.
источник
with open
? Как это решает вопрос об использовании имен файлов против файловых объектов?with open
правда? И что вы эффективно защищаете, так это функцию, которая использует только файловые объекты и не заботится, откуда она взялась?