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?

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'

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


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')

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.


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 .


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.


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.


-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 :)


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.