Wie prüfe ich, ob eine Datei existiert oder nicht, ohne die try
Aussage?
Im Allgemeinen ist es keine gute Praxis, Variablen genauso zu benennen wie Methodennamen.
Wie prüfe ich, ob eine Datei existiert oder nicht, ohne die try
Aussage?
Obwohl fast alle Möglichkeiten in (mindestens einer) der vorhandenen Antworten aufgeführt sind (z.B. Python 3.4 spezifische Dinge hinzugefügt wurden), werde ich versuchen, alles zusammenzufassen.
Note : jedes Stück von Python Der Code der Standardbibliothek, den ich veröffentlichen werde, gehört zur Version 3.5.3 .
Problemstellung :
Mögliche Lösungen :
[Python 3]: os.path. existiert ( Pfad ) (Überprüfen Sie auch andere Funktionsfamilienmitglieder wie os.path.isfile
, os.path.isdir
, os.path.lexists
für leicht unterschiedliche Verhaltensweisen)
os.path.exists(path)
Rückkehr
True
si Pfad verweist auf einen vorhandenen Pfad oder einen offenen Dateideskriptor. RückgabeFalse
für defekte symbolische Links. Auf einigen Plattformen gibt diese Funktion möglicherweiseFalse
wenn keine Erlaubnis zur Ausführung erteilt wird os.stat() auf die angeforderte Datei, auch wenn die Pfad physisch existiert.
Alles gut, aber wenn man dem Importbaum folgt:
os.path
- posixpath.py ( ntpath.py )
genericpath.py , Zeile ~#20+
def exists(path):
"""Test whether a path exists. Returns False for broken symbolic links"""
try:
st = os.stat(path)
except os.error:
return False
return True
es ist nur ein Versuchen Sie / außer Block um [Python 3]: os. stat ( Pfad, *, dir_fd=None, follow_symlinks=True ) . Ihr Code lautet also Versuchen Sie / außer frei, aber weiter unten im Framestack gibt es (mindestens) eine solchen Block. Dies gilt auch für andere Funktionen ( einschließlich os.path.isfile
).
1.1. [Python 3]: Pfad. ist_datei ()
Unter der Haube macht es genau die gleiche Sache ( pathlib.py , Zeile ~#1330 ):
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
[Python 3]: Mit Anweisungskontext-Managern . Entweder:
Erstellen Sie eine:
class Swallow: # Dummy example
swallowed_exceptions = (FileNotFoundError,)
def __enter__(self):
print("Entering...")
def __exit__(self, exc_type, exc_value, exc_traceback):
print("Exiting:", exc_type, exc_value, exc_traceback)
return exc_type in Swallow.swallowed_exceptions # only swallow FileNotFoundError (not e.g. TypeError - if the user passes a wrong argument like None or float or ...)
Und seine Verwendung - ich wiederhole die os.path.isfile
Verhalten (beachten Sie, dass dies nur zu Demonstrationszwecken ist, tun no Versuch, einen solchen Code zu schreiben für Produktion ):
import os
import stat
def isfile_seaman(path): # Dummy func
result = False
with Swallow():
result = stat.S_ISREG(os.stat(path).st_mode)
return result
Utilice [Python 3]: contextlib. unterdrücken. ( *Ausnahmen ) - das war speziell konzipiert für die selektive Unterdrückung von Ausnahmen
Aber sie scheinen Hüllen zu sein über Versuchen Sie / außer / sonst / schließlich Blöcke, wie [Python 3]: Die mit Anweisung Staaten:
Dies ermöglicht gemeinsame Versuchen Sie ... außer ... schließlich Nutzungsmuster zur bequemen Wiederverwendung gekapselt werden.
Funktionen zur Durchquerung des Dateisystems (und Suche der Ergebnisse nach übereinstimmenden Elementen)
[Python 3]: os. listdir ( path='.' ) (oder [Python 3]: os. scandir ( path='.' ) en Python v 3.5 +, Rückport: [PyPI]: scandir )
Unter der Haube verwenden beide:
über [GitHub]: python/cpython - (master) cpython/Modules/posixmodule.c
使用方法 scandir() 代わりに listdir() kann die Leistung von Code, der auch Informationen zum Dateityp oder zu den Dateiattributen benötigt, erheblich steigern, da os.DirEntry Objekte geben diese Informationen preis, wenn das Betriebssystem sie beim Scannen eines Verzeichnisses bereitstellt. Alle os.DirEntry Methoden können einen Systemaufruf durchführen, aber is_dir() y is_file() erfordern normalerweise nur einen Systemaufruf für symbolische Links; os.DirEntry.stat() erfordert unter Unix immer einen Systemaufruf, unter Windows jedoch nur einen für symbolische Links.
[Python 3]: os. Spaziergang ( top, topdown=True, onerror=None, followlinks=False )
os.listdir
( os.scandir
wenn verfügbar)[Python 3]: glob. iglob ( Pfadname, *, rekursiv=False ) (oder dessen Vorgänger: glob.glob
)
os.listdir
Da diese über Ordner iterieren, sind sie (in den meisten Fällen) ineffizient für unser Problem (es gibt Ausnahmen, wie nicht wildcarded Globus bing - wie @ShadowRanger bemerkte), also werde ich nicht darauf bestehen. Ganz zu schweigen davon, dass in einigen Fällen die Verarbeitung von Dateinamen erforderlich sein könnte.
[Python 3]: os. Zugang ( path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True ) deren Verhalten ähnlich ist wie os.path.exists
(tatsächlich ist er breiter, hauptsächlich wegen der 2 und Argument)
Benutzerrechte könnte die "Sichtbarkeit" der Datei einschränken, wie es im Dokument heißt:
...prüfen, ob der aufrufende Benutzer den angegebenen Zugriff auf Pfad . Modus sollte sein F_OK um die Existenz des Pfades zu testen...
os.access("/tmp", os.F_OK)
Da ich auch in der C Ich verwende diese Methode ebenfalls, denn sie ruft unter der Haube einheimische API s (wiederum über "${PYTHON_SRC_DIR}/Modules/posixmodule.c" ), aber es öffnet auch ein Tor für mögliche Benutzerfehler und es ist nicht so Python ic als andere Varianten. Wie @AaronHall richtig bemerkt hat, sollten Sie es also nur verwenden, wenn Sie wissen, was Sie tun:
Note : Einheimische anrufen API s ist auch möglich über [Python 3]: ctypes - Eine fremde Funktionsbibliothek für Python aber in den meisten Fällen ist es komplizierter.
( Gewinnen Sie spezifisch): Seit vcruntime* ( msvcr* ) .dll Ausfuhren einer [MS.Docs]: _access, _waccess Funktion Familie auch, hier ein Beispiel:
Python 3.5.3 (v3.5.3:1880cb95a742, Jan 16 2017, 16:02:32) [MSC v.1900 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import os, ctypes >>> ctypes.CDLL("msvcrt")._waccess(u"C:\\Windows\\System32\\cmd.exe", os.F_OK) 0 >>> ctypes.CDLL("msvcrt")._waccess(u"C:\\Windows\\System32\\cmd.exe.notexist", os.F_OK) -1
Anmerkungen :
os.F_OK
im Aufruf, aber das dient nur der Klarheit (sein Wert ist 0 )Le site Lnx ( Ubtu (16 x64) ) auch das Gegenstück:
Python 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import os, ctypes >>> ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6").access(b"/tmp", os.F_OK) 0 >>> ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6").access(b"/tmp.notexist", os.F_OK) -1
Anmerkungen :
Stattdessen Hardcoding libc den Pfad ( "/lib/x86_64-linux-gnu/libc.so.6" ), die von System zu System unterschiedlich sein können (und höchstwahrscheinlich auch sein werden), Keine (oder die leere Zeichenkette) kann an CDLL Konstrukteur ( ctypes.CDLL(None).access(b"/tmp", os.F_OK)
). Nach [man7]: DLOPEN(3) :
Si Dateiname NULL ist, dann ist der zurückgegebene Handle für die Haupt Programm. Bei der Übergabe an dlsym () wird mit diesem Handle eine Suche nach einem Symbol im Hauptprogramm, gefolgt von allen gemeinsam genutzten Objekten, die beim Programmstart geladen wurden, und dann alle gemeinsam genutzten Objekte, die von dlopen () mit dem Kennzeichen RTLD_GLOBAL .
__declspec(dllexport)
(warum in aller Welt die regelmäßig Person würde das tun?), das Hauptprogramm ist zwar ladbar, aber so gut wie unbrauchbarInstallieren Sie ein Modul eines Drittanbieters mit Dateisystemfunktionen
Höchstwahrscheinlich wird sie sich auf eine der oben genannten Möglichkeiten verlassen (vielleicht mit leichten Anpassungen).
Ein Beispiel wäre (wiederum, Gewinnen Sie spezifisch) [GitHub]: mhammond/pywin32 - Python für Windows (pywin32) Erweiterungen die ein Python Umhüllung über WINAPI s.
Da dies aber eher eine Notlösung ist, höre ich hier auf.
Eine andere (lahme) Abhilfe ( gainarie ) ist (wie ich es gerne nenne) die sysadmin Ansatz: Verwendung Python als Wrapper zur Ausführung von Shell-Befehlen
Gewinnen Sie :
(py35x64_test) e:\Work\Dev\StackOverflow\q000082831>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" -c "import os; print(os.system('dir /b \"C:\\Windows\\System32\\cmd.exe\" > nul 2>&1'))" 0 (py35x64_test) e:\Work\Dev\StackOverflow\q000082831>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" -c "import os; print(os.system('dir /b \"C:\\Windows\\System32\\cmd.exe.notexist\" > nul 2>&1'))" 1
Nix ( Lnx ( Ubtu )):
[cfati@cfati-ubtu16x64-0:~]> python3 -c "import os; print(os.system('ls \"/tmp\" > /dev/null 2>&1'))" 0 [cfati@cfati-ubtu16x64-0:~]> python3 -c "import os; print(os.system('ls \"/tmp.notexist\" > /dev/null 2>&1'))" 512
Unterm Strich :
Schlussbemerkung(en) :
Können Sie diese Aussage näher erläutern? "Obwohl es keine gute Praxis ist, verwende ich os.F_OK in dem Aufruf, aber das ist nur der Klarheit halber (sein Wert ist 0)"
@sk8asd123: Es ist etwas schwierig, das in einem Kommentar zu schreiben: Im Allgemeinen ist es am besten, Konstanten mit Funktionen zu verwenden, mit denen sie zusammenkommen. Das gilt, wenn man mit mehreren Modulen arbeitet, die dieselbe Konstante definieren, denn einige könnten nicht auf dem neuesten Stand sein, und es ist am besten, wenn die Funktionen und Konstanten synchronisiert sind. Bei der Arbeit mit ctypes (direkter Aufruf der Funktionen) hätte ich die Konstante (aus MSDN ), oder überhaupt keine Konstante verwenden. Es ist nur eine Richtlinie, die ich verwende, in 99,9% macht es wahrscheinlich keinen Unterschied (funktional).
@CristiFati: Ab dem 3.6, glob.iglob
(und glob.glob
auch) basieren auf os.scandir
Um den ersten Treffer in einem Verzeichnis mit 10 Mio. Dateien zu finden, scannen Sie nur so lange, bis Sie den ersten Treffer erreicht haben. Und sogar vor 3.6, wenn Sie glob
Methoden ohne Platzhalter, ist die Funktion intelligent: Sie weiß, dass es nur einen Treffer geben kann, also wird das Globbing vereinfacht, so dass nur noch os.path.isdir
o os.path.lexists
(je nachdem, ob der Pfad mit /
).
Python 3.4+ verfügt über ein objektorientiertes Pfadmodul: pathlib . Mit diesem neuen Modul können Sie wie folgt prüfen, ob eine Datei existiert:
import pathlib
p = pathlib.Path('path/to/file')
if p.is_file(): # or p.is_dir() to see if it is a directory
# do stuff
Sie können (und sollten in der Regel) weiterhin eine try/except
Block beim Öffnen von Dateien:
try:
with p.open() as f:
# do awesome stuff
except OSError:
print('Well darn.')
Das pathlib-Modul hat eine Menge cooler Sachen drauf: bequemes Globbing, Überprüfung des Dateibesitzers, einfacheres Verbinden von Pfaden, usw. Es lohnt sich, es auszuprobieren. Wenn Sie ein älteres Python verwenden (Version 2.6 oder später), können Sie pathlib trotzdem mit pip installieren:
# installs pathlib2 on older Python versions
# the original third-party module, pathlib, is no longer maintained.
pip install pathlib2
Dann importieren Sie es wie folgt:
# Older Python versions
import pathlib2 as pathlib
Dies ist der einfachste Weg, um zu prüfen, ob eine Datei existiert. Einfach denn die Datei existierte, als Sie sie prüften, nicht Garantie dass er da ist, wenn Sie ihn öffnen müssen.
import os
fname = "foo.txt"
if os.path.isfile(fname):
print("file does exist at this time")
else:
print("no such file exists at this time")
Solange Sie beabsichtigen, auf die Datei zuzugreifen, ist die Race Condition gibt es unabhängig davon, wie Ihr Programm aufgebaut ist. Ihr Programm kann nicht garantieren, dass ein anderer Prozess auf dem Computer die Datei nicht verändert hat. Dies bezeichnet Eric Lippert als eine exogene Ausnahme . Sie können dies nicht vermeiden, indem Sie vorher prüfen, ob die Datei vorhanden ist.
@IsaacSupeene Die beste Praxis besteht darin, das Fenster für (Datei-)Operationen so klein wie möglich zu halten, gefolgt von einer angemessenen Ausnahmebehandlung
Wie prüfe ich mit Python, ob eine Datei existiert, ohne eine try-Anweisung zu verwenden?
Jetzt verfügbar seit Python 3.4, importieren und instanziieren Sie eine Path
Objekt mit dem Dateinamen, und prüfen Sie die is_file
Methode (beachten Sie, dass diese auch für Symlinks, die auf reguläre Dateien verweisen, True zurückgibt):
>>> from pathlib import Path
>>> Path('/').is_file()
False
>>> Path('/initrd.img').is_file()
True
>>> Path('/doesnotexist').is_file()
False
Wenn Sie Python 2 verwenden, können Sie das pathlib-Modul von pypi zurückportieren, pathlib2
oder anderweitig prüfen isfile
von der os.path
Modul:
>>> import os
>>> os.path.isfile('/')
False
>>> os.path.isfile('/initrd.img')
True
>>> os.path.isfile('/doesnotexist')
False
Nun ist das oben genannte wahrscheinlich die beste pragmatische direkte Antwort hier, aber es gibt die Möglichkeit einer Race-Condition (je nachdem, was Sie versuchen zu erreichen), und die Tatsache, dass die zugrunde liegende Implementierung verwendet eine try
, aber Python verwendet try
überall in seiner Umsetzung.
Da Python die try
gibt es eigentlich keinen Grund, eine Implementierung zu vermeiden, die sie verwendet.
Der Rest dieser Antwort versucht jedoch, diese Vorbehalte zu berücksichtigen.
Verfügbar seit Python 3.4, verwenden Sie die neue Path
Objekt in pathlib
. Beachten Sie, dass .exists
ist nicht ganz richtig, denn Verzeichnisse sind keine Dateien (außer in dem Unix-Sinn, dass todo ist eine Datei).
>>> from pathlib import Path
>>> root = Path('/')
>>> root.exists()
True
Wir müssen also Folgendes verwenden is_file
:
>>> root.is_file()
False
Hier ist die Hilfe zu is_file
:
is_file(self)
Whether this path is a regular file (also True for symlinks pointing
to regular files).
Nehmen wir also eine Datei, von der wir wissen, dass sie eine Datei ist:
>>> import tempfile
>>> file = tempfile.NamedTemporaryFile()
>>> filepathobj = Path(file.name)
>>> filepathobj.is_file()
True
>>> filepathobj.exists()
True
Standardmäßig, NamedTemporaryFile
löscht die Datei, wenn sie geschlossen wird (und schließt sie automatisch, wenn es keine Verweise mehr auf sie gibt).
>>> del file
>>> filepathobj.exists()
False
>>> filepathobj.is_file()
False
Wenn Sie sich mit die Umsetzung Sie werden jedoch feststellen, dass 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
Wir mögen try
weil es Race Conditions vermeidet. Mit try
Sie versuchen einfach, Ihre Datei zu lesen, in der Erwartung, dass sie vorhanden ist, und wenn nicht, fangen Sie die Ausnahme ab und führen das Fallback-Verhalten aus, das sinnvoll ist.
Wenn Sie prüfen wollen, ob eine Datei vorhanden ist, bevor Sie versuchen, sie zu lesen, und Sie sie möglicherweise löschen und mehrere Threads oder Prozesse verwenden oder ein anderes Programm von dieser Datei weiß und sie löschen könnte, riskieren Sie einen Rennbedingung wenn Sie prüfen, ob es existiert, denn Sie sind dann Rennsport zu öffnen, bevor die Zustand (seine Existenz) ändert.
Race Conditions sind sehr schwer zu debuggen, weil es ein sehr kleines Zeitfenster gibt, in dem sie das Programm zum Scheitern bringen können.
Aber wenn das Ihre Motivation ist, sollten Sie puede den Wert von a erhalten try
Anweisung durch Verwendung der suppress
Kontext-Manager.
suppress
Python 3.4 gibt uns die suppress
Kontextmanager (früher der ignore
Kontextmanager), der semantisch genau das Gleiche in weniger Zeilen leistet und gleichzeitig (zumindest oberflächlich) die ursprüngliche Forderung erfüllt, eine try
Erklärung:
from contextlib import suppress
from pathlib import Path
Verwendung:
>>> with suppress(OSError), Path('doesnotexist').open() as f:
... for line in f:
... print(line)
...
>>>
>>> with suppress(OSError):
... Path('doesnotexist').unlink()
...
>>>
Für frühere Pythons können Sie Ihre eigenen Rollen verwenden suppress
, aber ohne eine try
wird ausführlicher sein als mit. Ich glaube dies ist die einzige Antwort, die nicht die try
auf einer beliebigen Ebene in der Python die vor Python 3.4 angewendet werden kann, weil sie stattdessen einen Kontextmanager verwendet:
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)
Vielleicht ist es mit einem Versuch einfacher:
from contextlib import contextmanager
@contextmanager
def suppress(*exceptions):
try:
yield
except exceptions:
pass
isfile
import os
os.path.isfile(path)
von der docs :
os.path.isfile(path)
Gibt True zurück, wenn der Pfad eine bestehende reguläre Datei ist. Dies folgt symbolischen Links, so dass sowohl
islink()
yisfile()
für denselben Pfad wahr sein kann.
Aber wenn man sich die Quelle dieser Funktion sehen Sie, dass sie tatsächlich eine Try-Anweisung verwendet:
# 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
Alles, was er tut, ist, den angegebenen Pfad zu verwenden, um zu sehen, ob er Statistiken über ihn erhalten kann, indem er OSError
und prüft dann, ob es sich um eine Datei handelt, wenn sie keine Ausnahme ausgelöst hat.
Wenn Sie beabsichtigen, etwas mit der Datei zu tun, würde ich vorschlagen, es direkt mit einem try-except zu versuchen, um eine Race Condition zu vermeiden:
try:
with open(path) as f:
f.read()
except OSError:
pass
os.access
Verfügbar für Unix und Windows ist os.access
zu verwenden, aber Sie müssen Flags übergeben, und es wird nicht zwischen Dateien und Verzeichnissen unterschieden. Dies wird eher dazu verwendet, um zu testen, ob der aufrufende Benutzer in einer Umgebung mit erhöhten Rechten Zugriff hat:
import os
os.access(path, os.F_OK)
Es leidet auch unter denselben Problemen mit Race Conditions wie isfile
. Von der docs :
Anmerkung: Die Verwendung von access() zur Überprüfung, ob ein Benutzer berechtigt ist, z.B. eine Datei zu öffnen zu öffnen, bevor er dies mit open() tatsächlich tut, schafft eine Sicherheitslücke, weil denn der Benutzer könnte die kurze Zeitspanne zwischen der Überprüfung und dem Öffnen der Datei ausnutzen, um sie zu manipulieren. Es ist vorzuziehen, EAFP Techniken. Zum Beispiel:
if os.access("myfile", os.R_OK): with open("myfile") as fp: return fp.read() return "some default data"
ist besser geschrieben als:
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()
Vermeiden Sie die Verwendung von os.access
. Es handelt sich um eine Funktion auf niedriger Ebene, bei der mehr Möglichkeiten für Benutzerfehler bestehen als bei den oben beschriebenen Objekten und Funktionen auf höherer Ebene.
Eine andere Antwort sagt dies über os.access
:
Ich persönlich bevorzuge diese Variante, weil sie unter der Haube native APIs aufruft (über "${PYTHON_SRC_DIR}/Modules/posixmodule.c"), aber sie öffnet auch ein Tor für mögliche Benutzerfehler, und sie ist nicht so pythonisch wie andere Varianten:
Diese Antwort besagt, dass sie eine nicht-pythonische, fehleranfällige Methode bevorzugt, ohne dass dies begründet wird. Sie scheint die Benutzer zu ermutigen, Low-Level-APIs zu verwenden, ohne sie zu verstehen.
Außerdem wird ein Kontextmanager erstellt, der durch die bedingungslose Rückgabe von True
erlaubt alle Ausnahmen (einschließlich KeyboardInterrupt
y SystemExit
!) stillschweigend zu passieren, was eine gute Möglichkeit ist, Fehler zu verstecken.
Dies scheint die Nutzer zu schlechten Praktiken zu verleiten.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
16 Stimmen
Um zu prüfen, ob ein Path-Objekt existiert, unabhängig davon, ob es sich um eine Datei oder ein Verzeichnis handelt, verwenden Sie
my_path.exists()
.1 Stimmen
my_path.exists()
ist nicht ausreichend.my_path.is_file()
wird Ihnen sagen, ob es sich um eine Datei handelt (könnte zum Lesen gut sein). Aber wenn Sie die Datei erstellen wollen, müssen Sie auch prüfenexists
schließen Sie also Verzeichnisse oder andere Dinge im Dateisystem, die keine Dateien sind, aus, die den Fehler verursachen.0 Stimmen
Verwenden Sie os.path.isfile, um nur Dateien zu prüfen, und verwenden Sie os.path.exists, um sowohl Dateien als auch Verzeichnisse zu prüfen. Mehr dazu erfahren Sie hier: shortbuzz.in/blog/shortbuzz.in/