Wstęp do pakowania:
Zanim będziesz mógł się martwić czytaniem plików zasobów, pierwszym krokiem jest upewnienie się, że pliki danych są pakowane do Twojej dystrybucji - łatwo jest je odczytać bezpośrednio z drzewa źródłowego, ale ważną częścią jest upewnij się, że te pliki zasobów są dostępne z kodu w zainstalowanym pakiecie.
Zorganizuj swój projekt w ten sposób, umieszczając pliki danych w podkatalogu w pakiecie:
.
├── package
│ ├── __init__.py
│ ├── templates
│ │ └── temp_file
│ ├── mymodule1.py
│ └── mymodule2.py
├── README.rst
├── MANIFEST.in
└── setup.py
Należy przekazać include_package_data=Truedo setup()rozmowy. Plik manifestu jest potrzebny tylko wtedy, gdy chcesz używać setuptools / distutils i kompilować dystrybucje źródła. Aby upewnić się, że templates/temp_filezostanie spakowany dla tej przykładowej struktury projektu, dodaj wiersz podobny do tego w pliku manifestu:
recursive-include package *
Notatka historyczna: Używanie pliku manifestu nie jest potrzebne w przypadku nowoczesnych backendów kompilacji, takich jak flit czy poezja, które domyślnie będą zawierać pliki danych pakietu. Tak więc, jeśli używasz pyproject.tomli nie masz setup.pypliku, możesz zignorować wszystkie rzeczy MANIFEST.in.
Teraz, po usunięciu opakowania, w części do czytania ...
Rekomendacje:
Użyj standardowych pkgutilinterfejsów API biblioteki . W kodzie biblioteki będzie wyglądać tak:
# within package/mymodule1.py, for example
import pkgutil
data = pkgutil.get_data(__name__, "templates/temp_file")
print("data:", repr(data))
text = pkgutil.get_data(__name__, "templates/temp_file").decode()
print("text:", repr(text))
Działa w zamkach błyskawicznych. Działa na Pythonie 2 i Pythonie 3. Nie wymaga zależności innych firm. Nie znam żadnych wad (jeśli tak, to skomentuj odpowiedź).
Złe sposoby uniknięcia:
Zły sposób nr 1: użycie ścieżek względnych z pliku źródłowego
To jest obecnie akceptowana odpowiedź. W najlepszym przypadku wygląda to mniej więcej tak:
from pathlib import Path
resource_path = Path(__file__).parent / "templates"
data = resource_path.joinpath("temp_file").read_bytes()
print("data", repr(data))
Co z tym jest nie tak? Założenie, że masz dostępne pliki i podkatalogi, nie jest poprawne. To podejście nie działa, jeśli wykonuje się kod, który jest spakowany w zipie lub kółku i może być całkowicie poza kontrolą użytkownika, czy pakiet zostanie w ogóle wyodrębniony do systemu plików.
Zły sposób nr 2: używanie interfejsów API pkg_resources
Jest to opisane w odpowiedzi z największą liczbą głosów. Wygląda mniej więcej tak:
from pkg_resources import resource_string
data = resource_string(__name__, "templates/temp_file")
print("data", repr(data))
Co z tym jest nie tak? Dodaje zależność od środowiska wykonawczego do setuptools , która powinna być zależna tylko od czasu instalacji . Importowanie i używanie pkg_resourcesmoże stać się naprawdę powolne, ponieważ kod tworzy działający zestaw wszystkich zainstalowanych pakietów, nawet jeśli interesowały Cię tylko własne zasoby pakietów. To nie jest wielka sprawa w czasie instalacji (ponieważ instalacja jest jednorazowa), ale jest brzydka w czasie wykonywania.
Zły sposób nr 3: używanie interfejsów API importlib.resources
To jest obecnie zalecenie w odpowiedzi z największą liczbą głosów. Jest to niedawny dodatek do biblioteki standardowej ( nowość w Pythonie 3.7 ), ale dostępny jest również backport. To wygląda tak:
try:
from importlib.resources import read_binary
from importlib.resources import read_text
except ImportError:
# Python 2.x backport
from importlib_resources import read_binary
from importlib_resources import read_text
data = read_binary("package.templates", "temp_file")
print("data", repr(data))
text = read_text("package.templates", "temp_file")
print("text", repr(text))
Co z tym jest nie tak? Cóż, niestety, to nie działa ... jeszcze. Jest to wciąż niepełny interfejs API, użycie importlib.resourcesbędzie wymagało dodania pustego pliku templates/__init__.py, aby pliki danych znajdowały się w pakiecie podrzędnym, a nie w podkatalogu. package/templatesUjawni również podkatalog jako samodzielny package.templatespakiet podrzędny, który można zaimportować . Jeśli to nic wielkiego i ci to nie przeszkadza, możesz dodać __init__.pytam plik i użyć systemu importu, aby uzyskać dostęp do zasobów. Jednak gdy już to robisz, równie dobrze możesz zamienić go w my_resources.pyplik i po prostu zdefiniować kilka bajtów lub zmiennych łańcuchowych w module, a następnie zaimportować je w kodzie Pythona. Tak czy inaczej, to system importu wykonuje tu ciężkie podnoszenie.
Przykładowy projekt:
Stworzyłem przykładowy projekt na github i załadowałem na PyPI , który demonstruje wszystkie cztery omówione powyżej podejścia. Wypróbuj z:
$ pip install resources-example
$ resources-example
Więcej informacji można znaleźć pod adresem https://github.com/wimglenn/resources-example .