내 Python 패키지에있는 파일을 어떻게 읽을 수 있는지 말해 주시겠습니까?
내 상황
내가로드하는 패키지에는 프로그램 내에서로드하려는 여러 템플릿 (문자열로 사용되는 텍스트 파일)이 있습니다. 그러나 그러한 파일의 경로를 어떻게 지정합니까?
다음에서 파일을 읽고 싶다고 상상해보십시오.
package\templates\temp_file
어떤 종류의 경로 조작? 패키지 기본 경로 추적?
내 Python 패키지에있는 파일을 어떻게 읽을 수 있는지 말해 주시겠습니까?
내가로드하는 패키지에는 프로그램 내에서로드하려는 여러 템플릿 (문자열로 사용되는 텍스트 파일)이 있습니다. 그러나 그러한 파일의 경로를 어떻게 지정합니까?
다음에서 파일을 읽고 싶다고 상상해보십시오.
package\templates\temp_file
어떤 종류의 경로 조작? 패키지 기본 경로 추적?
답변:
[2016-06-15 추가 : 분명히 모든 상황에서 작동하는 것은 아닙니다. 다른 답변을 참조하십시오]
import os, mypackage
template = os.path.join(mypackage.__path__[0], 'templates', 'temp_file')
importlib.resources
모듈 을 사용하십시오 .그만큼 전통 pkg_resources
에서setuptools
더 이상 사용하지 않는 것이 좋습니다 새로운 방법 때문에 :
setuptools
)이 Python의 표준 라이브러리에만 의존 .기존 코드를 이식 할 때 새 방법과의 차이점을 설명하기 위해 먼저 나열된 기존 코드를 유지했습니다 ( 여기 에서도 이식 설명 ).
템플릿이 모듈의 패키지 내에 중첩 된 폴더에 있다고 가정 해 보겠습니다.
<your-package>
+--<module-asking-the-file>
+--templates/
+--temp_file <-- We want this file.
참고 1 : 확실히, 우리는
__file__
속성을 조작 (예 : zip에서 제공 될 때 코드가 손상됨).2 주 : 이 패키지를 빌드하는 경우로 데이터 파일을 declatre 기억
package_data
또는data_files
당신을에서setup.py
.
pkg_resources
from 사용setuptools
(느림) 사용setuptools 배포판의 pkg_resources
패키지를 사용할 수 있지만 성능면 에서 비용이 발생 합니다 .
import pkg_resources
# Could be any dot-separated package/module name or a "Requirement"
resource_package = __name__
resource_path = '/'.join(('templates', 'temp_file')) # Do not use os.path.join()
template = pkg_resources.resource_string(resource_package, resource_path)
# or for a file-like stream:
template = pkg_resources.resource_stream(resource_package, resource_path)
팁 :
배포판이 압축되어 있어도 데이터를 읽으므로
zip_safe=True
에서 설정setup.py
하거나 python-3.5 에서 오랫동안 기다려온zipapp
패커 를 사용하여 자체 포함 된 배포판을 만들 수 있습니다.
setuptools
런타임 요구 사항 에 추가하는 것을 잊지 마십시오 (예 : install_requires`).
... 그리고 Setuptools / pkg_resources
문서 에 따르면 다음을 사용해서는 안됩니다 os.path.join
.
기본 리소스 액세스
리소스 이름은
/
경로로 구분되어야하며 절대적 (예 : 선행 없음/
)이거나 "..
" 와 같은 상대 이름을 포함 할 수 없습니다 . 마십시오 하지 사용os.path
은 그대로, 자원 경로를 조작하는 루틴을 하지 파일 시스템 경로.
importlib_resources
라이브러리 사용위의 보다 효율적인 표준 라이브러리 importlib.resources
모듈 을 사용하십시오 setuptools
.
try:
import importlib.resources as pkg_resources
except ImportError:
# Try backported to PY<37 `importlib_resources`.
import importlib_resources as pkg_resources
from . import templates # relative-import the *package* containing the templates
template = pkg_resources.read_text(templates, 'temp_file')
# or for a file-like stream:
template = pkg_resources.open_text(templates, 'temp_file')
주의:
기능에 대하여
read_text(package, resource)
:
- 그만큼
package
문자열이나 모듈이 될 수 있습니다.- 는
resource
더 이상 경로,하지만 기존 패키지에서 열 수있는 자원, 단지 파일 이름이 아니다; 경로 구분 기호를 포함 할 수 없으며 하위 리소스가 없을 수도 있습니다 (즉, 디렉터리가 될 수 없음).
질문에서 묻는 예의 경우 이제 다음을 수행해야합니다.
<your_package>/templates/
빈을 생성하여, 적절한 패키지에__init__.py
거기에 파일을import
문을 (더 이상 패키지 / 모듈 이름을 구문 분석하지 않음).resource_name = "temp_file"
(경로 없음)을 요청하십시오 .팁 :
- 현재 모듈 내부의 파일에 액세스하려면 패키지 인수를하는 설정
__package__
, 예를 들어,pkg_resources.read_text(__package__, 'temp_file')
( @ ben-mares 덕분에).- 때 상황이 재미가 될 실제 파일 이름이 함께 요구되는
path()
지금 상황에 관리자가 일시적으로 생성 된 파일 (읽기에 사용되기 때문에, 이 ).- 와, 조건부 이전의 파이를 들어, 백 포트 라이브러리 추가
install_requires=[" importlib_resources ; python_version<'3.7'"]
(확인 이 당신이 프로젝트를 패키징하는 경우setuptools<36.2.1
).- 기존 방법에서 마이그레이션 한 경우 런타임 요구 사항
setuptools
에서 라이브러리 를 제거해야합니다 .- 사용자 정의 할 기억
setup.py
이나MANIFEST
하는 정적 파일이 포함됩니다 .- 당신은 또한
zip_safe=True
당신의setup.py
.
NotImplementedError: Can't perform this operation for loaders without 'get_data()'
아이디어가 떠오르나요?
importlib.resources
하고 pkg_resources
있습니다 반드시 호환되지 않습니다 . , setuptools에 importlib.resources
추가 된 zip 파일과 함께 작동하며 에그 파일 자체가 추가되는 디렉토리에 저장된 zip 파일 인 egg 파일과 함께 작동합니다 . 예를 들어 , 에그는에 들어가 지만에있는 패키지 도 가져올 수 있습니다. .NET의 패키지에서 데이터를 추출하는 데 사용할 수 없습니다 . setuptools 가 계란 작업에 필요한 로더를 등록하는지 확인하지 않았습니다 . sys.path
pkg_resources
sys.path
sys.path = [..., '.../foo', '.../bar.zip']
.../foo
bar.zip
pkg_resources
bar.zip
importlib.resources
Package has no location
가 발생하면 추가 setup.py 구성이 필요 합니까?
templates
예제 와 같은 하위 모듈이 아님)에있는 파일에 액세스 하려면 package
인수를로 설정할 수 있습니다 __package__
. 예pkg_resources.read_text(__package__, 'temp_file')
리소스 파일 읽기에 대해 걱정하기 전에 첫 번째 단계는 데이터 파일이 처음에 배포판에 패키징되었는지 확인하는 것입니다. 소스 트리에서 직접 쉽게 읽을 수 있지만 중요한 부분은 이러한 리소스 파일이 설치된 패키지 내의 코드에서 액세스 할 수 있는지 확인하십시오 .
다음과 같이 프로젝트를 구조화하여 데이터 파일을 패키지 내의 하위 디렉토리에 넣습니다 .
.
├── 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 *
역사적인 cruft note : 기본적으로 패키지 데이터 파일을 포함하는 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")
print("data:", repr(data))
text = pkgutil.get_data(__name__, "templates/temp_file").decode()
print("text:", repr(text))
그것은 zip에서 작동합니다. Python 2 및 Python 3에서 작동합니다. 타사 종속성이 필요하지 않습니다. 나는 실제로 어떤 단점도 알고 있지 않습니다 (당신이 있다면 대답에 대해 언급하십시오).
이것은 현재 허용되는 답변입니다. 기껏해야 다음과 같이 보입니다.
from pathlib import Path
resource_path = Path(__file__).parent / "templates"
data = resource_path.joinpath("temp_file").read_bytes()
print("data", repr(data))
그게 무슨 문제입니까? 사용 가능한 파일 및 하위 디렉토리가 있다는 가정이 올바르지 않습니다. 이 접근 방식은 zip 또는 wheel로 압축 된 코드를 실행하는 경우에는 작동하지 않으며 패키지가 파일 시스템으로 추출되는지 여부에 관계없이 완전히 사용자가 제어 할 수 없습니다.
이것은 최고 투표 답변에 설명되어 있습니다. 다음과 같이 보입니다.
from pkg_resources import resource_string
data = resource_string(__name__, "templates/temp_file")
print("data", repr(data))
그게 무슨 문제입니까? setuptools 에 대한 런타임 종속성을 추가합니다. 이는 바람직하게는 설치 시간 종속성 이어야 합니다. 코드가 설치된 모든 패키지 의 작업 세트를 구축하기 때문에 가져 오기 및 사용 이 정말 느려질 수 있습니다.pkg_resources
패키지 리소스 . 설치시에는 큰 문제는 아니지만 (설치가 한 번만 종료되기 때문에) 런타임에는보기 흉합니다.
이것은 현재 최다 투표 답변의 권장 사항입니다. 최근 표준 라이브러리 추가 ( Python 3.7의 새로운 기능 )이지만 백 포트도 사용할 수 있습니다. 다음과 같이 보입니다.
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))
그게 무슨 문제입니까? 글쎄, 안타깝게도 아직 작동하지 않습니다. 이는 여전히 불완전한 API이므로을 사용 importlib.resources
하면 templates/__init__.py
데이터 파일이 하위 디렉토리가 아닌 하위 패키지 내에 상주 하도록 빈 파일을 추가해야합니다 . 또한 package/templates
하위 디렉토리를 package.templates
자체적으로 가져올 수있는 하위 패키지 로 노출합니다 . 그다지 큰 문제가 아니고 불편하지 않다면 계속해서 __init__.py
파일을 추가 하고 가져 오기 시스템을 사용하여 리소스에 액세스 할 수 있습니다. 그러나 그 동안 my_resources.py
파일 로 만들고 모듈에서 일부 바이트 또는 문자열 변수를 정의한 다음 Python 코드로 가져 오는 것이 좋습니다. 여기서 어느 쪽이든 무거운 작업을 수행하는 것은 수입 시스템입니다.
github 에서 예제 프로젝트를 만들고 PyPI에 업로드했습니다 . 위에서 설명한 네 가지 접근 방식을 모두 보여줍니다. 다음과 같이 사용해보십시오.
$ pip install resources-example
$ resources-example
자세한 내용은 https://github.com/wimglenn/resources-example 을 참조 하십시오 .
importlib.resources
이미 지원 중단 대기중인 불완전한 API로 이러한 모든 단점에도 불구하고 왜 추천 하시겠습니까? 새로운 것이 반드시 더 나은 것은 아닙니다. 나에게 실제로 제공합니까 어떤 장점 당신의 대답은에 대한 언급을하지 않는 다음 stdlib pkgutil, 이상?
pkgutil.get_data()
이는 저개발되고 더 이상 사용되지 않을 API입니다. 즉, 나는 당신에게 동의 importlib.resources
하지만, PY3.10이이 문제를 해결할 때까지 나는이 선택을지지합니다. Heving은 그것이 문서에서 권장하는 또 다른 "표준"이 아니라는 것을 알게되었습니다.
pkgutil
의 지원 중단 일정에 전혀 언급되지 않았 으며 정당한 이유없이 제거 될 가능성이 낮습니다. Python 2.3 이후로 사용되었으며 PEP 302 에서 로더 프로토콜의 일부로 지정되었습니다 . "과소 정의 된 API"를 사용하는 것은 대부분의 Python 표준 라이브러리를 설명 할 수있는 설득력있는 답변이 아닙니다!
이 구조가있는 경우
lidtk
├── bin
│ └── lidtk
├── lidtk
│ ├── analysis
│ │ ├── char_distribution.py
│ │ └── create_cm.py
│ ├── classifiers
│ │ ├── char_dist_metric_train_test.py
│ │ ├── char_features.py
│ │ ├── cld2
│ │ │ ├── cld2_preds.txt
│ │ │ └── cld2wili.py
│ │ ├── get_cld2.py
│ │ ├── text_cat
│ │ │ ├── __init__.py
│ │ │ ├── README.md <---------- say you want to get this
│ │ │ └── textcat_ngram.py
│ │ └── tfidf_features.py
│ ├── data
│ │ ├── __init__.py
│ │ ├── create_ml_dataset.py
│ │ ├── download_documents.py
│ │ ├── language_utils.py
│ │ ├── pickle_to_txt.py
│ │ └── wili.py
│ ├── __init__.py
│ ├── get_predictions.py
│ ├── languages.csv
│ └── utils.py
├── README.md
├── setup.cfg
└── setup.py
이 코드가 필요합니다.
import pkg_resources
# __name__ in case you're within the package
# - otherwise it would be 'lidtk' in this example as it is the package name
path = 'classifiers/text_cat/README.md' # always use slash
filepath = pkg_resources.resource_filename(__name__, path)
이상한 "항상 슬래시 사용"부분은 setuptools
API 에서 비롯됩니다.
또한 경로를 사용하는 경우 Windows를 사용하는 경우에도 경로 구분 기호로 슬래시 (/)를 사용해야합니다. Setuptools는 빌드시 슬래시를 적절한 플랫폼 별 구분 기호로 자동 변환합니다.
문서가 어디에 있는지 궁금한 경우 :
Python Cookbook의 "10.8. Reading Datafiles Within a Package"의 내용은 David Beazley와 Brian K. Jones가 답변을 제공합니다.
여기로 가져 오겠습니다.
다음과 같이 구성된 파일이 포함 된 패키지가 있다고 가정합니다.
mypackage/
__init__.py
somedata.dat
spam.py
이제 spam.py 파일이 somedata.dat 파일의 내용을 읽으려고한다고 가정합니다. 이를 수행하려면 다음 코드를 사용하십시오.
import pkgutil
data = pkgutil.get_data(__package__, 'somedata.dat')
결과 변수 데이터는 파일의 원시 내용을 포함하는 바이트 문자열입니다.
get_data ()의 첫 번째 인수는 패키지 이름이 포함 된 문자열입니다. 직접 제공하거나 다음과 같은 특수 변수를 사용할 수 있습니다.__package__
. 두 번째 인수는 패키지 내 파일의 상대 이름입니다. 필요한 경우 최종 디렉토리가 패키지 내에있는 한 표준 Unix 파일 이름 규칙을 사용하여 다른 디렉토리로 이동할 수 있습니다.
이런 식으로 패키지는 디렉토리, .zip 또는 .egg로 설치할 수 있습니다.
패키지의 모든 Python 모듈에는 __file__
속성이 있습니다.
다음과 같이 사용할 수 있습니다.
import os
from mypackage
templates_dir = os.path.join(os.path.dirname(mypackage.__file__), 'templates')
template_file = os.path.join(templates_dir, 'template.txt')
계란 리소스는 http://peak.telecommunity.com/DevCenter/PythonEggs#accessing-package-resources를 참조하십시오.
달걀 파일을 사용한다고 가정합니다. 추출되지 않음 :
나는 최근 프로젝트에서 달걀 (zip 파일)의 템플릿을 파일 시스템의 적절한 디렉토리로 추출하는 postinstall 스크립트를 사용하여이 문제를 "해결"했습니다. 작업 한 이후 가장 빠르고 신뢰할 수있는 솔루션이었습니다.__path__[0]
때때로 잘못 될 수 (이름은 기억 나지 않지만 목록 앞에 무언가를 추가 한 라이브러리가 하나 이상 있습니다!).
또한 계란 파일은 일반적으로 "달걀 캐시"라는 임시 위치로 즉석에서 추출됩니다. 스크립트를 시작하기 전이나 나중에 환경 변수를 사용하여 해당 위치를 변경할 수 있습니다.
os.environ['PYTHON_EGG_CACHE'] = path
그러나 작업을 제대로 수행 할 수있는 pkg_resources 가 있습니다.