Awal pengemasan:
Sebelum Anda bahkan khawatir tentang membaca file sumber daya, langkah pertama adalah memastikan bahwa file data telah dikemas ke dalam distribusi Anda - mudah untuk membacanya langsung dari struktur pohon sumber, tetapi bagian yang penting adalah membuat pastikan file sumber daya ini dapat diakses dari kode dalam paket yang diinstal .
Susun proyek Anda seperti ini, letakkan file data ke dalam subdirektori di dalam paket:
.
├── package
│ ├── __init__.py
│ ├── templates
│ │ └── temp_file
│ ├── mymodule1.py
│ └── mymodule2.py
├── README.rst
├── MANIFEST.in
└── setup.py
Anda harus lulus include_package_data=True
dalam setup()
panggilan. File manifes hanya diperlukan jika Anda ingin menggunakan setuptools / distutils dan membangun distribusi sumber. Untuk memastikan templates/temp_file
paket tersebut dikemas untuk contoh struktur proyek ini, tambahkan baris seperti ini ke dalam file manifes:
recursive-include package *
Catatan penting historis: Menggunakan file manifes tidak diperlukan untuk backend build modern seperti flit, poetry, yang akan menyertakan file data paket secara default. Jadi, jika Anda menggunakan pyproject.toml
dan tidak memiliki setup.py
file maka Anda dapat mengabaikan semua hal tentang MANIFEST.in
.
Sekarang, dengan mengemasnya, ke bagian bacaan ...
Rekomendasi:
Gunakan pkgutil
API perpustakaan standar . Ini akan terlihat seperti ini di kode perpustakaan:
# 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))
Ini berfungsi dalam ritsleting. Ia bekerja pada Python 2 dan Python 3. Ia tidak membutuhkan ketergantungan pihak ketiga. Saya tidak benar-benar mengetahui kerugian apa pun (jika ya, silakan komentari jawabannya).
Cara buruk untuk menghindari:
Cara buruk # 1: menggunakan jalur relatif dari file sumber
Saat ini adalah jawaban yang diterima. Paling banter, tampilannya seperti ini:
from pathlib import Path
resource_path = Path(__file__).parent / "templates"
data = resource_path.joinpath("temp_file").read_bytes()
print("data", repr(data))
Apa yang salah dengan itu? Asumsi bahwa Anda memiliki file dan subdirektori tidak benar. Pendekatan ini tidak berfungsi jika menjalankan kode yang dikemas dalam zip atau roda, dan mungkin sepenuhnya di luar kendali pengguna apakah paket Anda diekstrak ke sistem file atau tidak.
Cara buruk # 2: menggunakan pkg_resources API
Ini dijelaskan dalam jawaban pilihan teratas. Ini terlihat seperti ini:
from pkg_resources import resource_string
data = resource_string(__name__, "templates/temp_file")
print("data", repr(data))
Apa yang salah dengan itu? Ini menambahkan dependensi runtime pada setuptools , yang seharusnya hanya dependensi waktu instal . Mengimpor dan menggunakan pkg_resources
bisa menjadi sangat lambat, karena kode membangun satu set yang berfungsi dari semua paket yang diinstal, meskipun Anda hanya tertarik pada sumber paket Anda sendiri . Itu bukan masalah besar pada waktu penginstalan (karena penginstalan hanya sekali), tetapi jelek saat runtime.
Cara buruk # 3: Menggunakan API importlib.resources
Saat ini, ini adalah rekomendasi dalam jawaban pilihan teratas. Ini adalah tambahan pustaka standar baru-baru ini ( baru di Python 3.7 ), tetapi ada backport yang tersedia juga. Ini terlihat seperti ini:
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))
Apa yang salah dengan itu? Sayangnya, itu belum berhasil ... Ini masih merupakan API yang tidak lengkap, penggunaan importlib.resources
akan mengharuskan Anda untuk menambahkan file kosong templates/__init__.py
agar file data akan berada di dalam sub-paket daripada di subdirektori. Ini juga akan mengekspos package/templates
subdirektori sebagai package.templates
sub-paket yang dapat diimpor dengan sendirinya. Jika itu bukan masalah besar dan itu tidak mengganggu Anda, Anda dapat melanjutkan dan menambahkan __init__.py
file di sana dan menggunakan sistem impor untuk mengakses sumber daya. Namun, saat Anda melakukannya, Anda dapat membuatnya menjadi my_resources.py
file, dan cukup mendefinisikan beberapa byte atau variabel string dalam modul, lalu mengimpornya dalam kode Python. Ini adalah sistem impor yang melakukan pekerjaan berat di sini.
Contoh proyek:
Saya telah membuat proyek contoh di github dan mengunggahnya di PyPI , yang menunjukkan keempat pendekatan yang dibahas di atas. Cobalah dengan:
$ pip install resources-example
$ resources-example
Lihat https://github.com/wimglenn/resources-example untuk info lebih lanjut.