Können Sie mir sagen, wie ich eine Datei lesen kann, die sich in meinem Python-Paket befindet?
Meine Situation
Ein Paket, das ich lade, enthält eine Reihe von Vorlagen (Textdateien, die als Zeichenfolgen verwendet werden), die ich aus dem Programm heraus laden möchte. Aber wie gebe ich den Pfad zu einer solchen Datei an?
Stellen Sie sich vor, ich möchte eine Datei lesen aus:
package\templates\temp_file
Eine Art Pfadmanipulation? Paketbasispfadverfolgung?
Antworten:
[hinzugefügt am 15.06.2016: Anscheinend funktioniert dies nicht in allen Situationen. Bitte beachten Sie die anderen Antworten]
quelle
TLDR; Verwenden Sie das
importlib.resources
Modul der Standardbibliothek, wie in der folgenden Methode Nr. 2 erläutert.Das traditionelle
pkg_resources
vonsetuptools
wird nicht mehr empfohlen, da die neue Methode:setuptools
) benötigen , sondern sich nur auf die Standardbibliothek von Python verlassen.Ich habe das traditionelle zuerst aufgelistet, um die Unterschiede mit der neuen Methode beim Portieren von vorhandenem Code zu erklären (Portierung auch hier erklärt ).
Angenommen, Ihre Vorlagen befinden sich in einem Ordner, der im Paket Ihres Moduls verschachtelt ist:
1) Verwenden von
pkg_resources
fromsetuptools
(langsam)Sie können ein
pkg_resources
Paket aus der Setuptools- Distribution verwenden, dies ist jedoch in Bezug auf die Leistung mit Kosten verbunden :... und beachten Sie, dass Sie laut Setuptools /
pkg_resources
docs nicht verwenden solltenos.path.join
:2) Python> = 3.7 oder Verwendung der zurückportierten
importlib_resources
BibliothekVerwenden Sie das
importlib.resources
Modul der Standardbibliothek, das effizienter ist alssetuptools
oben:Für das in der Frage gestellte Beispiel müssen wir jetzt:
<your_package>/templates/
zu einem richtigen Paket, indem Sie eine leere__init__.py
Datei darin erstellen.import
Anweisung verwenden (keine Analyse von Paket- / Modulnamen mehr).resource_name = "temp_file"
(kein Weg).quelle
NotImplementedError: Can't perform this operation for loaders without 'get_data()'
Ideen?importlib.resources
undpkg_resources
sind nicht unbedingt kompatibel .importlib.resources
funktioniert mit hinzugefügtensys.path
Zip-pkg_resources
Dateien , Setuptools und mit Egg-Dateien, die Zip-Dateien sind, die in einem Verzeichnis gespeichert sind, das selbst hinzugefügt wirdsys.path
. ZB mitsys.path = [..., '.../foo', '.../bar.zip']
gehen Eier rein.../foo
, aber Pakete inbar.zip
können auch importiert werden. Sie können nicht verwendenpkg_resources
, um Daten aus Paketen in zu extrahierenbar.zip
. Ich habe nicht überprüft, ob setuptools den erforderlichen Lader fürimportlib.resources
die Arbeit mit Eiern registriert .Package has no location
auftritt?templates
nach dem Beispiel), dann können Sie das Setpackage
Argument__package__
, zBpkg_resources.read_text(__package__, 'temp_file')
Ein Verpackungsvorspiel:
Bevor Sie sich überhaupt um das Lesen von Ressourcendateien kümmern können, müssen Sie zunächst sicherstellen, dass die Datendateien überhaupt in Ihre Distribution gepackt werden. Es ist einfach, sie direkt aus dem Quellbaum zu lesen, aber der wichtige Teil ist das Erstellen Stellen Sie sicher, dass auf diese Ressourcendateien über Code in einem installierten Paket zugegriffen werden kann.
Strukturieren Sie Ihr Projekt folgendermaßen und fügen Sie Datendateien in ein Unterverzeichnis innerhalb des Pakets ein:
Sie sollten
include_package_data=True
densetup()
Anruf weiterleiten. Die Manifestdatei wird nur benötigt, wenn Sie setuptools / distutils verwenden und Quelldistributionen erstellen möchten.templates/temp_file
Fügen Sie der Manifestdatei eine Zeile wie diese hinzu, um sicherzustellen, dass das Paket für diese Beispielprojektstruktur gepackt wird:Historischer Cruft-Hinweis: Die Verwendung einer Manifestdatei ist für moderne Build-Backends wie Flit oder Poetry nicht erforderlich , die standardmäßig die Paketdatendateien enthalten. Wenn Sie
pyproject.toml
also einesetup.py
Datei verwenden und keine haben , können Sie alles ignorierenMANIFEST.in
.Nun, mit der Verpackung aus dem Weg, auf den Leseteil ...
Empfehlung:
Verwenden Sie Standard-Bibliotheks-
pkgutil
APIs. Im Bibliothekscode wird es so aussehen:Es funktioniert in Reißverschlüssen. Es funktioniert unter Python 2 und Python 3. Es sind keine Abhängigkeiten von Drittanbietern erforderlich. Ich bin mir keiner Nachteile bewusst (wenn ja, dann kommentieren Sie bitte die Antwort).
Schlechte Möglichkeiten zu vermeiden:
Schlechter Weg Nr. 1: Verwenden relativer Pfade aus einer Quelldatei
Dies ist derzeit die akzeptierte Antwort. Bestenfalls sieht es ungefähr so aus:
Was stimmt damit nicht? Die Annahme, dass Dateien und Unterverzeichnisse verfügbar sind, ist nicht korrekt. Dieser Ansatz funktioniert nicht, wenn Code ausgeführt wird, der in einer Zip-Datei oder einem Rad gepackt ist, und es liegt möglicherweise völlig außerhalb der Kontrolle des Benutzers, ob Ihr Paket überhaupt in ein Dateisystem extrahiert wird oder nicht.
Schlechter Weg # 2: Verwenden von pkg_resources-APIs
Dies ist in der am besten bewerteten Antwort beschrieben. Es sieht ungefähr so aus:
Was stimmt damit nicht? Es wird eine Laufzeitabhängigkeit von setuptools hinzugefügt , die vorzugsweise nur eine Installationszeitabhängigkeit sein sollte. Das Importieren und Verwenden
pkg_resources
kann sehr langsam werden, da der Code einen funktionierenden Satz aller installierten Pakete erstellt, obwohl Sie nur an Ihren eigenen Paketressourcen interessiert waren . Das ist zur Installationszeit keine große Sache (da die Installation einmalig ist), aber zur Laufzeit ist es hässlich.Schlechter Weg Nr. 3: Verwenden der APIs importlib.resources
Dies ist derzeit die Empfehlung in der am besten bewerteten Antwort. Es handelt sich um eine neue Standardbibliothek ( neu in Python 3.7 ), es ist jedoch auch ein Backport verfügbar. Es sieht aus wie das:
Was stimmt damit nicht? Nun, leider funktioniert es nicht ... noch nicht. Dies ist immer noch eine unvollständige API.
importlib.resources
Wenn Sie diese verwenden, müssen Sie eine leere Datei hinzufügentemplates/__init__.py
, damit sich die Datendateien in einem Unterpaket und nicht in einem Unterverzeichnis befinden. Daspackage/templates
Unterverzeichnis wird auch alspackage.templates
eigenständiges importierbares Unterpaket verfügbar gemacht . Wenn das keine große Sache ist und Sie nicht stört, können Sie die__init__.py
Datei dort hinzufügen und über das Importsystem auf Ressourcen zugreifen. Wenn Sie schon dabei sind, können Sie es auch zu einermy_resources.py
Datei machen und einfach einige Bytes oder String-Variablen im Modul definieren und sie dann in Python-Code importieren. Es ist das Importsystem, das hier so oder so das schwere Heben erledigt.Beispielprojekt:
Ich habe ein Beispielprojekt auf erstellt Github und hochgeladen auf PyPI , die alle vier demonstriert oben Ansätze diskutiert. Probieren Sie es aus mit:
Weitere Informationen finden Sie unter https://github.com/wimglenn/resources-example .
quelle
importlib.resources
trotz all dieser Mängel eine unvollständige API empfehlen, deren Verfall bereits aussteht ? Neu ist nicht unbedingt besser. Sagen Sie mir, welche Vorteile es tatsächlich gegenüber dem stdlib pkgutil bietet, über das Ihre Antwort keine Erwähnung findet?pkgutil.get_data()
mein Bauchgefühl bestätigt - es ist eine unterentwickelte, veraltete API. Das heißt, ich stimme Ihnen zu,importlib.resources
ist keine viel bessere Alternative, aber bis PY3.10 dies löst, stehe ich zu dieser Wahl und habe erfahren, dass es nicht nur ein weiterer "Standard" ist, der von den Dokumenten empfohlen wird.pkgutil
wird im Verfallsplan von PEP 594 - Entfernen leerer Batterien aus der Standardbibliothek überhaupt nicht erwähnt und wird wahrscheinlich nicht ohne guten Grund entfernt. Es gibt es seit Python 2.3 und es wurde als Teil des Loader-Protokolls in PEP 302 angegeben . Die Verwendung einer "unterdefinierten API" ist keine sehr überzeugende Antwort, die den Großteil der Python-Standardbibliothek beschreiben könnte!pkgutil
in jeder Hinsicht. Ihr "Bauchgefühl" und Ihr Appell an die Autorität sind für mich bedeutungslos. Wenn es Probleme mitget_data
Ladern gibt, zeigen Sie Beweise und praktische Beispiele.Falls Sie diese Struktur haben
Sie benötigen diesen Code:
Der seltsame Teil "Schrägstrich immer verwenden" stammt von
setuptools
APIsFalls Sie sich fragen, wo sich die Dokumentation befindet:
quelle
Der Inhalt in "10.8. Lesen von Datendateien in einem Paket" des Python-Kochbuchs, dritte Ausgabe von David Beazley und Brian K. Jones mit den Antworten.
Ich werde es einfach hierher bringen:
Angenommen, Sie haben ein Paket mit Dateien, die wie folgt organisiert sind:
Angenommen, die Datei spam.py möchte den Inhalt der Datei somedata.dat lesen. Verwenden Sie dazu den folgenden Code:
Die resultierenden Variablendaten sind eine Bytezeichenfolge, die den Rohinhalt der Datei enthält.
Das erste Argument für get_data () ist eine Zeichenfolge, die den Paketnamen enthält. Sie können es entweder direkt angeben oder eine spezielle Variable verwenden, z
__package__
. Das zweite Argument ist der relative Name der Datei im Paket. Bei Bedarf können Sie mithilfe der Standardkonventionen für Unix-Dateinamen in verschiedene Verzeichnisse navigieren, solange sich das endgültige Verzeichnis noch im Paket befindet.Auf diese Weise kann das Paket als Verzeichnis, .zip oder .egg installiert werden.
quelle
Jedes Python-Modul in Ihrem Paket hat ein
__file__
AttributSie können es verwenden als:
Informationen zu Eiressourcen finden Sie unter: http://peak.telecommunity.com/DevCenter/PythonEggs#accessing-package-resources
quelle
Angenommen, Sie verwenden eine Eidatei. nicht extrahiert:
Ich habe dies in einem kürzlich durchgeführten Projekt mithilfe eines Postinstall-Skripts "gelöst", das meine Vorlagen aus dem Ei (Zip-Datei) in das richtige Verzeichnis im Dateisystem extrahiert. Es war die schnellste und zuverlässigste Lösung, die ich gefunden habe, da die Arbeit mit
__path__[0]
manchmal schief gehen kann (ich erinnere mich nicht an den Namen, aber ich bin auf mindestens eine Bibliothek gestoßen, die etwas vor dieser Liste hinzugefügt hat!).Auch Eidateien werden normalerweise im laufenden Betrieb an einen temporären Speicherort extrahiert, der als "Eiercache" bezeichnet wird. Sie können diesen Speicherort mithilfe einer Umgebungsvariablen ändern, entweder bevor Sie Ihr Skript starten oder sogar später, z.
Es gibt jedoch pkg_resources , die den Job möglicherweise ordnungsgemäß ausführen .
quelle