Jak odnosić się do względnych ścieżek zasobów podczas pracy z repozytorium kodu

188

Pracujemy z repozytorium kodów, które jest wdrażane zarówno w systemie Windows, jak i Linux - czasami w różnych katalogach. W jaki sposób jeden z modułów w projekcie powinien odnosić się do jednego z zasobów innych niż Python w projekcie (pliki CSV itp.)?

Jeśli zrobimy coś takiego:

thefile=open('test.csv')

lub:

thefile=open('../somedirectory/test.csv')

Działa tylko wtedy, gdy skrypt jest uruchamiany z jednego określonego katalogu lub podzbioru katalogów.

Chciałbym zrobić coś takiego:

path=getBasePathOfProject()+'/somedirectory/test.csv'
thefile=open(path)

Czy to możliwe?

olamundo
źródło

Odpowiedzi:

255

Spróbuj użyć nazwy pliku względem bieżącej ścieżki plików. Przykład dla „./my_file”:

fn = os.path.join(os.path.dirname(__file__), 'my_file')

W Pythonie 3.4+ możesz także użyć pathlib :

fn = pathlib.Path(__file__).parent / 'my_file'
c089
źródło
3
Myślę, że to rozwiązanie będzie działać tylko wtedy, gdy zasób znajduje się w tym samym katalogu pliku python lub w jego podkatalogu. Jak rozwiązać ten problem, gdy masz następującą strukturę drzewa: / katalog_główny_projektu / katalog_plików python / niektóre dodatkowe podkatalogi tutaj py_file.py / resources / niektóre podkatalogi tutaj resource_file.csv
olamundo
1
Niestety, drzewo plików jest zniekształcone na tej ostatniej wiadomości ... druga próba: masz plik na /Project_Root_dir/python_files_dir/some_subdirs/py_file.py i masz plik zasobów na /Project_Root_dir/resources/some_subdirs/resource_file.csv
olamundo
28
Powinieneś być w stanie dostać się do katalogu nadrzędnego za pomocą join (foo, '..'). Więc z / root / python_files / module / myfile, użyj os.path.join (os.path.dirname ( __file__), '..', '..', 'resources')
c089
7
os.pardirjest nieco lepszy niż '..', chociaż oba są równoważne zarówno w systemie POSIX, jak i Windows.
davidchambers
4
@cedbeu: Jest to równoważne na każdym systemie, z jakim się zetknąłem i myślę, że każde systemowe python działa dzisiaj (popraw mnie, jeśli się mylę). Jeśli jednak oczekujesz, że Python zostanie przeniesiony do systemu przy użyciu innego separatora ścieżek w przyszłości i chcesz, aby Twój kod był na to gotowy, os.pardir będzie bardziej przenośny. Sprawiłbym, że każdy programista, nawet ten, który nigdy nie czyta żadnego pytona, zna znaczenie „..”, podczas gdy „os.pardir” to poziom pośredni, który należałoby sprawdzić w dokumentacji, więc ja osobiście ” d trzymać się „..”.
c089
40

Jeśli korzystasz z narzędzi instalacyjnych lub rozpowszechniasz (instalacja setup.py), wtedy „właściwym” sposobem dostępu do tych spakowanych zasobów wydaje się być użycie zasobów_pakietu.

W twoim przypadku byłby to przykład

import pkg_resources
my_data = pkg_resources.resource_string(__name__, "foo.dat")

Który oczywiście czyta zasób, a odczytane dane binarne byłyby wartością my_data

Jeśli potrzebujesz tylko nazwy pliku, której możesz użyć

resource_filename(package_or_requirement, resource_name)

Przykład:

resource_filename("MyPackage","foo.dat")

Zaletą jest to, że gwarantuje działanie, nawet jeśli jest to dystrybucja archiwum jak jajko.

Zobacz http://packages.python.org/distribute/pkg_resources.html#resourcemanager-api

Sharoon Thomas
źródło
3
Wiem, że to stara odpowiedź. Moim preferowanym sposobem jest (/ może być?) Użycie pkg_resources, ale czy po zniknięciu spakowanych jaj nie ma nic złego w korzystaniu __file__z starych dobrych czasów?
Pykler,
1
To solidne podejście. Nawet jeśli konwencja o jajach odchodzi, setuptools nie i wiele osób wciąż instaluje zabezpieczenia przed repozytoriami git, w których jajo jest budowane w czasie wykonywania
deepelement
18

W Pythonie ścieżki są względne do bieżącego katalogu roboczego , którym w większości przypadków jest katalog, z którego uruchamiany jest program. Bieżący katalog roboczy jest bardzo prawdopodobne, nie tak samo jak w katalogu pliku modułu, więc przy użyciu ścieżki względnej do bieżącego pliku modułu zawsze jest to zły wybór.

Najlepszym rozwiązaniem powinno być użycie ścieżki bezwzględnej:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')
bez nieba
źródło
15

Często używam czegoś podobnego do tego:

import os
DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'datadir'))

# if you have more paths to set, you might want to shorten this as
here = lambda x: os.path.abspath(os.path.join(os.path.dirname(__file__), x))
DATA_DIR = here('datadir') 

pathjoin = os.path.join
# ...
# later in script
for fn in os.listdir(DATA_DIR):
    f = open(pathjoin(DATA_DIR, fn))
    # ...

Zmienna

__file__

przechowuje nazwę pliku skryptu, w którym piszesz ten kod, dzięki czemu możesz tworzyć ścieżki w stosunku do skryptu, ale nadal zapisane ścieżkami bezwzględnymi. Działa całkiem dobrze z kilku powodów:

  • ścieżka jest absolutna, ale wciąż względna
  • projekt można nadal wdrożyć we względnym kontenerze

Ale musisz uważać na kompatybilność platformy - os.pathsep systemu Windows różni się od UNIX.

użytkownik137673
źródło
5
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)

Próbujesz również znormalizować swoje cwdużycie os.path.abspath(os.getcwd()). Więcej informacji tutaj .

gavoja
źródło
3
bardzo niewiele przypadków użycia, w których cwdścieżka jest modułem
cedbeu
nie działa w pakiecie, tylko z tego samego katalogu (lub katalogu roboczego) ustawionego przez skrypt.
Alexandra
To nie zadziała, jeśli użytkownik uruchomi program, używając bezwzględnej ścieżki z innego katalogu. np. python3 /usr/someone/test.py
sgrpwr
2

Możesz użyć __file__zmiennej wbudowanej. Zawiera ścieżkę bieżącego pliku. Zaimplementowałbym getBaseOfProject w module w katalogu głównym twojego projektu. Tam dostanę część ścieżki __file__i ją zwrócę. Tę metodę można następnie zastosować wszędzie w projekcie.

Achim
źródło
0

Trochę mnie tu utknęło. Chciał spakować niektóre pliki zasobów do pliku koła i uzyskać do nich dostęp. Czy pakiet używał pliku manifestu, ale instalacja przez pip nie instalowała go, chyba że był to podkatalog. Mam nadzieję, że te sceenowe strzały pomogą

├── cnn_client
   ├── image_preprocessor.py
   ├── __init__.py
   ├── resources
      ├── mscoco_complete_label_map.pbtxt
      ├── retinanet_complete_label_map.pbtxt
      └── retinanet_label_map.py
   ├── tf_client.py

MANIFEST.in

recursive-include cnn_client/resources *

Utworzono weel przy użyciu standardowego setup.py. pip zainstalował plik koła. Po instalacji sprawdzane są zasoby. Oni są

ls /usr/local/lib/python2.7/dist-packages/cnn_client/resources

mscoco_complete_label_map.pbtxt
retinanet_complete_label_map.pbtxt 
 retinanet_label_map.py  

W tfclient.py, aby uzyskać dostęp do tych plików. z

templates_dir = os.path.join(os.path.dirname(__file__), 'resources')
 file_path = os.path.join(templates_dir, \
            'mscoco_complete_label_map.pbtxt')
        s = open(file_path, 'r').read()

I to działa.

Alex Punnen
źródło
-5

Długo zastanawiałem się nad odpowiedzią na to pytanie, ale w końcu zrozumiałem (i to naprawdę bardzo proste):

import sys
import os
sys.path.append(os.getcwd() + '/your/subfolder/of/choice')

# now import whatever other modules you want, both the standard ones,
# as the ones supplied in your subfolders

Spowoduje to dołączenie względnej ścieżki twojego podfolderu do katalogów, w których Python może zajrzeć. Jest dość szybki i brudny, ale działa jak urok :)

Rutger Semp
źródło
6
Działa to tylko wtedy, gdy uruchamiasz program Python z tego samego katalogu, co dany plik .py. I w takim przypadku możesz po prostu to zrobić open('your/subfolder/of/choice').
Paul Fisher
4
a OP wspomniał, że kod musi działać zarówno w systemie Windows, jak i Linux. To nie będzie.
user183037