พรีลูดบรรจุภัณฑ์:
ก่อนที่คุณจะกังวลเกี่ยวกับการอ่านไฟล์ทรัพยากรขั้นตอนแรกคือตรวจสอบให้แน่ใจว่าไฟล์ข้อมูลได้รับการรวมเข้ากับการแจกจ่ายของคุณตั้งแต่แรกคุณสามารถอ่านได้โดยตรงจากแผนผังต้นทาง แต่ส่วนที่สำคัญคือการทำ แน่ใจว่าไฟล์ทรัพยากรเหล่านี้สามารถเข้าถึงได้จากโค้ดภายในแพ็คเกจที่ติดตั้ง
จัดโครงสร้างโครงการของคุณเช่นนี้โดยวางไฟล์ข้อมูลลงในไดเร็กทอรีย่อยภายในแพ็คเกจ:
.
├── package
│ ├── __init__.py
│ ├── templates
│ │ └── temp_file
│ ├── mymodule1.py
│ └── mymodule2.py
├── README.rst
├── MANIFEST.in
└── setup.py
คุณควรผ่านinclude_package_data=True
ในการsetup()
โทร ไฟล์รายการจำเป็นเฉพาะเมื่อคุณต้องการใช้ setuptools / distutils และสร้างการแจกแจงซอร์ส เพื่อให้แน่ใจว่าtemplates/temp_file
ได้รับแพ็กเกจสำหรับโครงสร้างโปรเจ็กต์ตัวอย่างนี้ให้เพิ่มบรรทัดเช่นนี้ลงในไฟล์รายการ:
recursive-include package *
บันทึกย่อทางประวัติศาสตร์: การใช้ไฟล์ manifest ไม่จำเป็นสำหรับแบ็กเอนด์บิลด์สมัยใหม่เช่น flit, poetry ซึ่งจะรวมไฟล์ข้อมูลแพ็กเกจไว้ตามค่าเริ่มต้น ดังนั้นถ้าคุณกำลังใช้pyproject.toml
และคุณไม่ได้มีไฟล์แล้วคุณสามารถละเว้นทุกสิ่งที่เกี่ยวกับsetup.py
MANIFEST.in
ตอนนี้มีบรรจุภัณฑ์ไม่อยู่ในส่วนการอ่าน ...
คำแนะนำ:
ใช้pkgutil
API ของไลบรารีมาตรฐาน จะมีลักษณะเช่นนี้ในรหัสห้องสมุด:
# within package/mymodule1.py, for example
import pkgutil
data = pkgutil.get_data(__name__, "templates/temp_file")
ทำงานเป็นซิป ทำงานบน Python 2 และ Python 3 ไม่ต้องการการอ้างอิงของบุคคลที่สาม ฉันไม่ทราบถึงข้อเสียใด ๆ (ถ้าคุณเป็นเช่นนั้นโปรดแสดงความคิดเห็นในคำตอบ)
วิธีหลีกเลี่ยงที่ไม่ดี:
วิธีที่ไม่ดี # 1: ใช้เส้นทางสัมพัทธ์จากไฟล์ต้นฉบับ
ขณะนี้เป็นคำตอบที่ยอมรับ อย่างดีที่สุดก็มีลักษณะดังนี้:
from pathlib import Path
resource_path = Path(__file__).parent / "templates"
data = resource_path.joinpath("temp_file").read_bytes()
มันผิดอะไร? สมมติฐานว่าคุณมีไฟล์และไดเร็กทอรีย่อยที่พร้อมใช้งานไม่ถูกต้อง วิธีนี้ใช้ไม่ได้หากเรียกใช้โค้ดที่บรรจุใน zip หรือ wheel และอาจอยู่นอกเหนือการควบคุมของผู้ใช้ไม่ว่าแพ็กเกจของคุณจะถูกแตกออกไปยังระบบไฟล์หรือไม่
วิธีที่ไม่ดี # 2: ใช้ pkg_resources API
สิ่งนี้อธิบายไว้ในคำตอบที่ได้รับการโหวตสูงสุด มีลักษณะดังนี้:
from pkg_resources import resource_string
data = resource_string(__name__, "templates/temp_file")
มันผิดอะไร? เพิ่มการพึ่งพารันไทม์บนsetuptoolsซึ่งควรเป็นการพึ่งพาเวลาติดตั้งเท่านั้น การนำเข้าและใช้งานpkg_resources
อาจช้ามากเนื่องจากโค้ดสร้างชุดที่ใช้งานได้ของแพ็คเกจที่ติดตั้งทั้งหมดแม้ว่าคุณจะสนใจเฉพาะทรัพยากรแพ็คเกจของคุณเองก็ตาม นั่นไม่ใช่เรื่องใหญ่ในเวลาติดตั้ง (เนื่องจากการติดตั้งครั้งเดียวปิด) แต่มันน่าเกลียดที่รันไทม์
วิธีที่ไม่ดี # 3: การใช้ importlib.resources APIs
นี่คือคำแนะนำในคำตอบที่ได้รับการโหวตสูงสุด เป็นการเพิ่มไลบรารีมาตรฐานล่าสุด( ใหม่ใน Python 3.7 ) ดูเหมือนว่า:
from importlib.resources import read_binary
data = read_binary("package.templates", "temp_file")
มันผิดอะไร? น่าเสียดายที่มันใช้ไม่ได้ ... นี่ยังคงเป็น API ที่ไม่สมบูรณ์การใช้importlib.resources
จะทำให้คุณต้องเพิ่มไฟล์เปล่าtemplates/__init__.py
เพื่อให้ไฟล์ข้อมูลนั้นอยู่ภายในแพ็กเกจย่อยแทนที่จะอยู่ในไดเร็กทอรีย่อย นอกจากนี้ยังแสดงpackage/templates
ไดเร็กทอรีย่อยเป็นpackage.templates
แพ็กเกจย่อยที่นำเข้าได้ในสิทธิ์ของตนเอง หากนั่นไม่ใช่เรื่องใหญ่และไม่รบกวนคุณคุณสามารถดำเนินการต่อและเพิ่ม__init__.py
ไฟล์ที่นั่นและใช้ระบบนำเข้าเพื่อเข้าถึงทรัพยากร อย่างไรก็ตามในขณะที่คุณกำลังทำอยู่คุณสามารถทำให้เป็นmy_resources.py
ไฟล์แทนได้และกำหนดไบต์หรือตัวแปรสตริงในโมดูลจากนั้นนำเข้าในโค้ด Python เป็นระบบนำเข้าที่ทำการยกของหนักที่นี่ไม่ว่าจะด้วยวิธีใดก็ตาม
รางวัลชมเชย: การใช้ importlib_resources API ที่ใหม่กว่า
สิ่งนี้ยังไม่ได้กล่าวถึงในคำตอบอื่น ๆ แต่importlib_resources
เป็นมากกว่า backport ธรรมดาของimportlib.resources
โค้ดPython 3.7+ มี API แบบข้ามได้ซึ่งคุณสามารถใช้ได้ดังนี้:
import importlib_resources
my_resources = importlib_resources.files("package")
data = (my_resources / "templates" / "temp_file").read_bytes()
สิ่งนี้ใช้ได้กับ Python 2 และ 3 ทำงานในซิปและไม่จำเป็นต้อง__init__.py
เพิ่มไฟล์ปลอมในไดเร็กทอรีย่อยของทรัพยากร ข้อเสียเพียงอย่างเดียวกับpkgutil
ที่ฉันเห็นคือ API ใหม่เหล่านี้ยังไม่มาถึง stdlib ดังนั้นจึงยังมีการพึ่งพาของบุคคลที่สาม API ใหม่กว่าimportlib_resources
ควรมาถึง stdlib importlib.resources
ใน Python 3.9
ตัวอย่างโครงการ:
ฉันได้สร้างโครงการตัวอย่างบนgithubและอัปโหลดบนPyPIซึ่งแสดงให้เห็นถึงแนวทางทั้งห้าที่กล่าวถึงข้างต้น ลองใช้กับ:
$ pip install resources-example
$ resources-example
ดูhttps://github.com/wimglenn/resources-exampleสำหรับข้อมูลเพิ่มเติม