Как проверить, существует ли файл с использованием Python без оператора try?
Теперь доступно с Python 3.4, импортируйте и создайте экземпляр объекта Path
с именем файла и проверьте метод is_file
(обратите внимание, что это возвращает True для символических ссылок, также указывающих на обычные файлы):
>>> from pathlib import Path
>>> Path('/').is_file()
False
>>> Path('/initrd.img').is_file()
True
>>> Path('/doesnotexist').is_file()
False
Если вы используете Python 2, вы можете перенести модуль pathlib обратно из pypi, pathlib2
или проверить isfile
в модуле os.path
:
>>> import os
>>> os.path.isfile('/')
False
>>> os.path.isfile('/initrd.img')
True
>>> os.path.isfile('/doesnotexist')
False
Теперь вышеприведенное, вероятно, является лучшим прагматическим прямым ответом здесь, но есть возможность условия гонки (в зависимости от того, что вы пытаетесь выполнить) и тот факт, что базовая реализация использует try
, но Python использует try
везде в своей реализации.
Поскольку Python везде использует try
, на самом деле нет причин избегать реализации, которая его использует.
Но остальная часть этого ответа пытается учесть эти предостережения.
Более длинный, гораздо более педантичный ответ
Доступно с Python 3.4, используйте новый Path
объект в pathlib
. Обратите внимание, что .exists
не совсем правильно, потому что каталоги не являются файлами (за исключением того, что все является файлом).
>>> from pathlib import Path
>>> root = Path('/')
>>> root.exists()
True
Итак, нам нужно использовать is_file
:
>>> root.is_file()
False
Вот справка по is_file
:
is_file(self)
Whether this path is a regular file (also True for symlinks pointing
to regular files).
Итак, давайте получим файл, который, как мы знаем, является файлом:
>>> import tempfile
>>> file = tempfile.NamedTemporaryFile()
>>> filepathobj = Path(file.name)
>>> filepathobj.is_file()
True
>>> filepathobj.exists()
True
По умолчанию NamedTemporaryFile
удаляет файл при закрытии (и автоматически закрывается, когда на него больше нет ссылок).
>>> del file
>>> filepathobj.exists()
False
>>> filepathobj.is_file()
False
Если вы углубитесь в реализацию , вы увидите, что is_file
использует try
:
def is_file(self):
"""
Whether this path is a regular file (also True for symlinks pointing
to regular files).
"""
try:
return S_ISREG(self.stat().st_mode)
except OSError as e:
if e.errno not in (ENOENT, ENOTDIR):
raise
# Path doesn't exist or is a broken symlink
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
return False
Условия гонки: почему мы любим попробовать
Нам нравится try
, потому что он избегает условий гонки. С try
вы просто пытаетесь прочитать свой файл, ожидая, что он там будет, а если нет, вы перехватываете исключение и выполняете любое резервное поведение, которое имеет смысл.
Если вы хотите проверить, что файл существует, прежде чем пытаться его прочитать, и, возможно, вы удаляете его, и тогда вы можете использовать несколько потоков или процессов, или другая программа знает об этом файле и может удалить его - вы рискуете шанс на состояние гонки , если вы проверите, что он существует, потому что тогда вы скачете , чтобы открыть его до того, как изменится условие (его существование).
Условия гонки очень трудно отлаживать, потому что есть очень маленькое окно, в котором они могут вызвать сбой вашей программы.
Но если это ваша мотивация, вы можете получить значение оператора try
с помощью диспетчера контекста suppress
.
Как избежать условий гонки без указания попытки: suppress
Python 3.4 предоставляет нам менеджер контекста suppress
(ранее менеджер контекста ignore
), который выполняет семантически точно то же самое в меньшем количестве строк, а также (в хотя бы поверхностно) встречая оригинал, просим избегать заявления try
:
from contextlib import suppress
from pathlib import Path
Использование:
>>> with suppress(OSError), Path('doesnotexist').open() as f:
... for line in f:
... print(line)
...
>>>
>>> with suppress(OSError):
... Path('doesnotexist').unlink()
...
>>>
Для более ранних Питонов, вы можете бросить свои собственные suppress
, но без try
будет более многословно, чем с. Я верю, что на самом деле это единственный ответ, который не использует try
на каком-либо уровне Python , который можно применить до Python 3.4, потому что вместо него используется менеджер контекста:
class suppress(object):
def __init__(self, *exceptions):
self.exceptions = exceptions
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is not None:
return issubclass(exc_type, self.exceptions)
Возможно, проще попробовать:
from contextlib import contextmanager
@contextmanager
def suppress(*exceptions):
try:
yield
except exceptions:
pass
Другие варианты, которые не отвечают запросу "без попытки":
ISFILE
import os
os.path.isfile(path)
из документов :
os.path.isfile(path)
Возвращает True, если путь - это существующий обычный файл. Это следует за символическим
ссылки, так что islink()
и isfile()
могут быть истинными для одного и того же пути.
Но если вы изучите источник этой функции, вы увидите, что она действительно использует оператор try:
# This follows symbolic links, so both islink() and isdir() can be true
# for the same path on systems that support symlinks
def isfile(path):
"""Test whether a path is a regular file"""
try:
st = os.stat(path)
except os.error:
return False
return stat.S_ISREG(st.st_mode)
>>> OSError is os.error
True
Все, что он делает, - использует заданный путь, чтобы посмотреть, сможет ли он получить статистику, перехватывает OSError
и затем проверяет, является ли это файл, если он не вызывает исключение.
Если вы намереваетесь что-то сделать с файлом, я бы предложил напрямую попробовать его с попыткой, за исключением того, чтобы избежать условия гонки:
try:
with open(path) as f:
f.read()
except OSError:
pass
os.access
Для Unix и Windows доступно os.access
, но для использования вы должны пройти флаги, и это не делает различий между файлами и каталогами. Это больше используется для проверки, имеет ли реальный вызывающий пользователь доступ в среде с повышенными привилегиями:
import os
os.access(path, os.F_OK)
Он также страдает от тех же проблем с расой, что и isfile
. Из документов :
Примечание:
Использование access () для проверки того, авторизован ли пользователь, например, открыть файл
прежде чем сделать это с помощью open () создаст дыру в безопасности, потому что
пользователь может использовать короткий промежуток времени между проверкой и
открыв файл, чтобы манипулировать им. Желательно использовать EAFP
методы. Например:
if os.access("myfile", os.R_OK):
with open("myfile") as fp:
return fp.read()
return "some default data"
лучше записать как:
try:
fp = open("myfile")
except IOError as e:
if e.errno == errno.EACCES:
return "some default data"
# Not a permission error.
raise
else:
with fp:
return fp.read()
Избегайте использования os.access
. Это функция низкого уровня, которая имеет больше возможностей для ошибок пользователя, чем объекты и функции более высокого уровня, описанные выше.
Критика другого ответа:
Другой ответ говорит об этом os.access
:
Лично я предпочитаю этот, потому что внутри он вызывает собственные API (через "$ {PYTHON_SRC_DIR} /Modules/posixmodule.c"), но он также открывает ворота для возможных ошибок пользователя, и он не такой Pythonic. как другие варианты:
В этом ответе говорится, что он предпочитает непифонический, подверженный ошибкам метод без объяснения причин. Похоже, это поощряет пользователей использовать низкоуровневые API, не понимая их.
Он также создает менеджер контекста, который, безоговорочно возвращая True
, позволяет всем исключениям (включая KeyboardInterrupt
и SystemExit
!) Проходить без вывода сообщений, что является хорошим способом скрыть ошибки.
Похоже, это поощряет пользователей применять плохую практику.