패키지 서브 디렉토리의 데이터에 액세스


130

./data/하위 디렉토리 에서 데이터 파일을 열어야하는 모듈로 파이썬 패키지를 작성 중 입니다. 지금은 클래스와 함수에 하드 코딩 된 파일 경로가 있습니다. 하위 시스템이 사용자 시스템에 설치된 위치에 관계없이 하위 디렉토리에 액세스 할 수있는보다 강력한 코드를 작성하고 싶습니다.

나는 다양한 방법을 시도했지만 지금까지 나는 운이 없었습니다. 대부분의 "현재 디렉토리"명령은 모듈의 디렉토리가 아닌 시스템의 파이썬 인터프리터의 디렉토리를 반환하는 것으로 보입니다.

이것은 사소하고 일반적인 문제인 것 같습니다. 그러나 나는 그것을 알아낼 수없는 것 같습니다. 문제의 일부는 내 데이터 파일이 파일이 아니므로 .py가져 오기 기능 등을 사용할 수 없다는 것입니다.

어떤 제안?

현재 내 패키지 디렉토리는 다음과 같습니다.

/
__init__.py
module1.py
module2.py
data/   
   data.txt

나는 액세스하려고하고 있어요 data.txt에서 module*.py!


답변:


24

다음 __file__과 같이 패키지 경로를 얻는 데 사용할 수 있습니다 .

import os
this_dir, this_filename = os.path.split(__file__)
DATA_PATH = os.path.join(this_dir, "data", "data.txt")
print open(DATA_PATH).read()

44
파일이 배포판 (IE. egg) 인 경우에는 작동하지 않습니다. pkg_resources를 사용하여 데이터 파일을 가져 오십시오.
Chris

2
실제로 이것은 깨졌습니다.
Federico

1
또한 __file__값은 zip 파일의 경로이므로 py2exe에서는 작동하지 않습니다.
포드

1
이것은 실제로 나를 위해 일했습니다. 문제가 없었습니다. 저는 파이썬 3.6을 사용하고 있습니다
Jorge

1
배포 (egg 등)의 경우에는 작동하지 않습니다.
Adarsh ​​Trivedi

166

이를 수행하는 표준 방법은 setuptools 패키지 및 pkg_resources를 사용하는 것입니다.

다음 계층 구조에 따라 패키지를 배치하고이 링크에 따라 패키지 설정 파일이 데이터 리소스를 가리 키도록 구성 할 수 있습니다.

http://docs.python.org/distutils/setupscript.html#installing-package-data

그런 다음이 링크에 따라 pkg_resources를 사용하여 해당 파일을 다시 찾아 사용할 수 있습니다.

http://peak.telecommunity.com/DevCenter/PkgResources#basic-resource-access

import pkg_resources

DATA_PATH = pkg_resources.resource_filename('<package name>', 'data/')
DB_FILE = pkg_resources.resource_filename('<package name>', 'data/sqlite.db')

7
pkg_resourcessetuptools에 런타임 종속성을 작성 하지 않습니까 ? 예를 들어, 데비안 꾸러미를 재배포해서 왜 그렇게 의존 python-setuptools해야합니까? 지금까지는 __file__잘 작동합니다.
mlt

4
왜 이런 일이 더 : 이러한 리소스 파일 및 디렉토리로 존재 또는 어떤 종류의 아카이브로 압축 여부는 ResourceManager 클래스, 패키지 자원에 균일 한 액세스를 제공합니다
vrdhn

4
훌륭한 제안, 감사합니다. from pkg_resources import resource_filename open(resource_filename('data', 'data.txt'), 'rb')
eageranalyst

5
패키지가 설치되어 있지 않은 경우 어떻게 사용합니까? 로컬에서 테스트 한 결과
Claudiu

11
파이썬 3.7에서는 (성능 문제 때문에)이 목적을 importlib.resources대신합니다 pkg_resources.
benjimin

13

오늘날 작동하는 솔루션을 제공합니다. 바퀴를 모두 재발 명하지 않으려면이 API를 사용하십시오.

실제 파일 시스템 파일 이름이 필요합니다. 압축 된 계란은 캐시 디렉토리로 추출됩니다.

from pkg_resources import resource_filename, Requirement

path_to_vik_logo = resource_filename(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")

지정된 리소스에 대해 읽을 수있는 파일과 유사한 객체를 반환합니다. 실제 파일, StringIO 또는 유사한 객체 일 수 있습니다. 스트림은 리소스에있는 바이트가 그대로 읽히도록 "바이너리 모드"에 있습니다.

from pkg_resources import resource_stream, Requirement

vik_logo_as_stream = resource_stream(Requirement.parse("enb.portals"), "enb/portals/reports/VIK_logo.png")

pkg_resources를 사용한 패키지 발견 및 자원 액세스


10

작동 하지 않는 코드를 자세히 설명하는 대답을 종종 지적하지는 않지만 예외라고 생각합니다. 파이썬 3.7 importlib.resources이이를 대체하기로했습니다 pkg_resources. 이름에 슬래시 가없는 패키지 내의 파일에 액세스하는 데 사용됩니다.

foo/
    __init__.py
    module1.py
    module2.py
    data/   
       data.txt
    data2.txt

즉 , 예를 들어 data2.txt패키지 내부에 액세스 할 수 있습니다foo

importlib.resources.open_binary('foo', 'data2.txt')

그러나 그것은 예외로 실패 할 것입니다.

>>> importlib.resources.open_binary('foo', 'data/data.txt')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.7/importlib/resources.py", line 87, in open_binary
    resource = _normalize_path(resource)
  File "/usr/lib/python3.7/importlib/resources.py", line 61, in _normalize_path
    raise ValueError('{!r} must be only a file name'.format(path))
ValueError: 'data/data2.txt' must be only a file name

이 배치 제외하고 수정할 수 없습니다 __init__.pydata패키지로 사용하여 다음과 :

importlib.resources.open_binary('foo.data', 'data.txt')

이 동작의 이유는 "디자인에 의한 것"입니다 . 그러나 디자인이 바뀔 수 있습니다 ...


텍스트가있는 YouTube 비디오보다 "디자인에 의한 것"에 대한 링크가 더 좋 습니까?
gerrit

@gerrit 두 번째 것은 텍스트를 포함합니다. "This was a deliberate choice, but I think you have a valid use case. @brettcannon what do you think? And if we allow this, should we make sure it gets into Python 3.7?"
Antti Haapala

8

전체 모듈의 이름이 필요합니다. 디렉토리 트리에는 세부 정보가 나와 있지 않습니다.

import pkg_resources
print(    
    pkg_resources.resource_filename(__name__, 'data/data.txt')
)

특히 setuptools는 압축 된 데이터 파일과 이름 일치를 기반으로 파일을 확인하지 않는 것처럼 보이 data/므로 어떤 경우에도 접두사를 거의 포함시켜야합니다 . os.path.join('data', 'data.txt)대체 디렉토리 구분 기호가 필요한 경우 사용할 수 있습니다 . 일반적으로 하드 코딩 된 유닉스 스타일 디렉토리 구분 기호와의 호환성 문제는 없습니다.


docs.python.org/3.6/distutils/… > 설정 스크립트에 제공된 모든 경로 이름 (파일 또는 디렉토리)은 슬래시로 구분 된 Unix 규칙을 사용하여 작성해야합니다. Distutils는이 플랫폼 중립적 표현을 실제로 경로 이름을 사용하기 전에 현재 플랫폼에서 적절한 것으로 변환합니다. 이를 통해 운영 체제 전체에서 설정 스크립트를 이식 할 수 있습니다. 물론 Distutils의 주요 목표 중 하나입니다. 이러한 의미에서이 문서의 모든 경로 이름은 슬래시로 구분됩니다.
changyuheng

6

나는 대답을 찾아 냈다고 생각합니다.

data_path.py 모듈을 만들고 다음을 포함하는 다른 모듈로 가져옵니다.

data_path = os.path.join(os.path.dirname(__file__),'data')

그런 다음 모든 파일을

open(os.path.join(data_path,'filename'), <param>)

2
리소스가 아카이브 배포 (예 : 압축 된 계란) 인 경우에는 작동하지 않습니다. 다음과 같은 것을 선호하십시오 :pkg_resources.resource_string('pkg_name', 'data/file.txt')
ankostis

@ankostis setuptools는 당신이 __file__어딘가에서 사용한 것을 발견하면 아카이브를 추출하기에 충분히 영리 합니다. 내 경우에는 실제로 스트림을 원하지 않고 경로를 원하는 라이브러리를 사용합니다. 물론 파일을 디스크에 임시로 쓸 수는 있지만 게으른 경우 setuptools의 기능을 사용합니다.
letmaik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.