Cara merujuk ke jalur relatif sumber daya saat bekerja dengan repositori kode

188

Kami sedang bekerja dengan repositori kode yang digunakan untuk Windows dan Linux - terkadang di direktori yang berbeda. Bagaimana seharusnya salah satu modul di dalam proyek merujuk ke salah satu sumber daya non-Python dalam proyek (file CSV, dll.)?

Jika kita melakukan sesuatu seperti:

thefile=open('test.csv')

atau:

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

Ini akan berfungsi hanya ketika skrip dijalankan dari satu direktori tertentu, atau bagian dari direktori.

Yang ingin saya lakukan adalah sesuatu seperti:

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

Apa itu mungkin?

olamundo
sumber

Jawaban:

255

Cobalah untuk menggunakan nama file relatif ke jalur file saat ini. Contoh untuk './my_file':

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

Di Python 3.4+ Anda juga bisa menggunakan pathlib :

fn = pathlib.Path(__file__).parent / 'my_file'
c089
sumber
3
Saya pikir solusi ini hanya akan berfungsi jika sumber daya berada di direktori yang sama dengan file python, atau di sub direktori itu. Bagaimana Anda menyelesaikannya ketika Anda memiliki struktur pohon berikut: / Project_Root_dir / python_files_dir / Beberapa lebih banyak subdirs di sini py_file.py / resource / beberapa subdirs di sini resource_file.csv
olamundo
1
Maaf, pohon file rusak pada pesan terakhir ... percobaan kedua: Anda memiliki file Anda di /Project_Root_dir/python_files_dir/some_subdirs/py_file.py dan Anda memiliki file sumber daya di /Project_Root_dir/resources/some_subdirs/resource_file.cs
olamundo
28
Anda harus bisa masuk ke direktori induk menggunakan join (foo, '..'). Jadi dari / root / python_files / module / myfile, gunakan os.path.join (os.path.dirname ( __file__), '..', '..', 'resources')
c089
7
os.pardirsedikit lebih baik daripada '..', meskipun keduanya setara pada POSIX dan Windows.
davidchambers
4
@cedbeu: Ini setara pada setiap sistem yang pernah saya temui dan saya pikir setiap sistem python berjalan pada hari ini (tolong perbaiki saya jika saya salah di sini). Namun, jika Anda berharap python akan porting ke sistem menggunakan pemisah jalur yang berbeda di masa depan dan ingin kode Anda siap untuk itu, os.pardir akan lebih portabel. Saya akan membuat kasus bahwa setiap programmer, bahkan orang yang tidak pernah membaca python pun tahu arti dari "..", sedangkan "os.pardir" adalah tingkat tipuan yang harus dilihat orang dalam dokumentasi secara pribadi. d berpegang pada "..".
c089
41

Jika Anda menggunakan alat setup atau mendistribusikan (instalasi setup.py) maka cara "benar" untuk mengakses sumber daya paket ini tampaknya menggunakan package_resources.

Dalam kasus Anda, contohnya adalah

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

Yang tentu saja membaca sumber daya dan data biner baca akan menjadi nilai my_data

Jika Anda hanya membutuhkan nama file, Anda juga dapat menggunakan

resource_filename(package_or_requirement, resource_name)

Contoh:

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

Keuntungannya adalah dijamin bekerja meskipun itu adalah distribusi arsip seperti telur.

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

Sharoon Thomas
sumber
3
Saya tahu ini adalah jawaban lama, cara yang saya sukai adalah (/ mungkin mungkin?) Menggunakan pkg_resources, tetapi dengan hilangnya telur zip, apakah ada salahnya menggunakan __file__seperti masa lalu yang indah?
Pykler
1
Ini adalah pendekatan yang solid. Bahkan jika konvensi telur akan hilang, setuptools tidak dan banyak masih menginstal deps terhadap repositori git di mana telur dibangun pada saat runtime
deepelement
18

Dalam Python, path relatif terhadap direktori kerja saat ini , yang dalam banyak kasus adalah direktori tempat Anda menjalankan program Anda. The direktori kerja saat ini ini sangat mungkin tidak sama dengan direktori file modul Anda, sehingga menggunakan path relatif ke file modul Anda saat ini selalu pilihan yang buruk.

Menggunakan jalur absolut harus menjadi solusi terbaik:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')
skyfree
sumber
15

Saya sering menggunakan sesuatu yang mirip dengan ini:

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))
    # ...

Variabel

__file__

menyimpan nama file skrip tempat Anda menulis kode itu, sehingga Anda dapat membuat jalur relatif ke skrip, tetapi masih ditulis dengan jalur absolut. Ini bekerja cukup baik karena beberapa alasan:

  • jalan itu absolut, tetapi masih relatif
  • proyek masih dapat digunakan dalam wadah relatif

Tetapi Anda perlu memperhatikan kompatibilitas platform - os.pathsep Windows berbeda dari UNIX.

pengguna137673
sumber
5
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)

Anda juga mencoba untuk menormalkan cwdpenggunaan Anda os.path.abspath(os.getcwd()). Info lebih lanjut di sini .

gavoja
sumber
3
sangat sedikit kasus penggunaan di mana cwdjalur modul,
cedbeu
itu tidak berfungsi di dalam sebuah paket, hanya dari direktori yang sama (atau direktori kerja) yang ditetapkan oleh skrip.
alexandra
Ini tidak akan berfungsi jika pengguna menjalankan program menggunakan jalur absolut dari direktori yang berbeda. misalnya python3 /usr/someone/test.py
sgrpwr
2

Anda bisa menggunakan __file__variabel build in . Ini berisi jalur file saat ini. Saya akan mengimplementasikan getBaseOfProject dalam sebuah modul di root proyek Anda. Di sana saya akan mendapatkan bagian jalur __file__dan akan mengembalikannya. Metode ini kemudian dapat digunakan di mana saja dalam proyek Anda.

Achim
sumber
0

Saya sedikit bingung di sini. Ingin mengemas beberapa file sumber daya ke file roda dan mengaksesnya. Apakah kemasan menggunakan file manifes, tetapi pemasangan pip tidak menginstalnya kecuali itu adalah sub direktori. Berharap tembakan-tembakan sceen ini akan membantu

├── 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 *

Membuat weel menggunakan setup.py standar. pip menginstal file roda. Setelah instalasi diperiksa apakah sumber daya diinstal. Mereka

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  

Di tfclient.py untuk mengakses file-file ini. dari

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

Dan itu berhasil.

Alex Punnen
sumber
-5

Saya menghabiskan waktu yang lama untuk mencari tahu jawabannya, tetapi akhirnya saya mendapatkannya (dan sebenarnya sangat sederhana):

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

Ini akan menambahkan path relatif dari subfolder Anda ke direktori untuk python untuk melihatnya. Ini cukup cepat dan kotor, tetapi bekerja seperti pesona :)

Rutger Semp
sumber
6
Ini hanya akan berfungsi jika Anda menjalankan program Python dari direktori yang sama dengan file .py yang dimaksud. Dan dalam hal ini, Anda bisa melakukannya open('your/subfolder/of/choice').
Paul Fisher
4
dan OP menyebutkan bahwa kode perlu bekerja pada Windows dan Linux. Ini tidak akan.
user183037