Jalur relatif dengan Python

245

Saya sedang membangun skrip pembantu sederhana untuk pekerjaan yang akan menyalin beberapa file template di basis kode kami ke direktori saat ini. Namun, saya tidak memiliki jalur absolut ke direktori tempat templat disimpan. Saya memang memiliki jalur relatif dari skrip tetapi ketika saya memanggil skrip itu memperlakukannya sebagai jalur relatif ke direktori kerja saat ini. Apakah ada cara untuk menentukan bahwa url relatif ini berasal dari lokasi skrip?

baudtack
sumber

Jawaban:

325

Di file yang memiliki skrip, Anda ingin melakukan sesuatu seperti ini:

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

Ini akan memberi Anda jalur absolut ke file yang Anda cari. Perhatikan bahwa jika Anda menggunakan setuptools, Anda sebaiknya menggunakan API sumber daya paketnya .

UPDATE : Saya menanggapi komentar di sini sehingga saya dapat menempelkan contoh kode. :-)

Apakah saya benar dalam berpikir bahwa __file__itu tidak selalu tersedia (mis. Ketika Anda menjalankan file secara langsung daripada mengimpornya)?

Saya berasumsi maksud Anda __main__skrip ketika Anda menyebutkan menjalankan file secara langsung. Jika demikian, sepertinya tidak demikian halnya pada sistem saya (python 2.5.1 pada OS X 10.5.7):

#foo.py
import os
print os.getcwd()
print __file__

#in the interactive interpreter
>>> import foo
/Users/jason
foo.py

#and finally, at the shell:
~ % python foo.py
/Users/jason
foo.py

Namun, saya tahu bahwa ada beberapa kebiasaan dengan __file__ekstensi C. Misalnya, saya bisa melakukan ini di Mac saya:

>>> import collections #note that collections is a C extension in Python 2.5
>>> collections.__file__
'/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-
dynload/collections.so'

Namun, ini menimbulkan pengecualian pada mesin Windows saya.

Jason Baker
sumber
1
Apakah saya benar dalam berpikir bahwa file tidak selalu tersedia (mis. Ketika Anda menjalankan file secara langsung daripada mengimpornya)?
Stephen Edmonds
@Stephen Edmonds Saya menggunakannya sebagai file yang saya jalankan, bukan impor, dan ini berfungsi dengan baik.
baudtack
22
Catatan Anda harus menggunakan os.path.join di mana-mana untuk mudah dibawa:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
ford
22
os.path.dirname(__file__)dapat memberikan string kosong, gunakan os.path.dirname(os.path.abspath(__file__))sebagai gantinya
Dmitry Trofimov
14
Ini hal kecil, tapi TOLONG jangan gunakan dir sebagai nama variabel karena itu adalah builtin.
David
63

yang Anda butuhkan os.path.realpath(contoh di bawah ini menambahkan direktori induk ke jalur Anda)

import sys,os
sys.path.append(os.path.realpath('..'))
pengguna989762
sumber
2
os.path.dirname(__file__)memberiku string kosong. Ini bekerja dengan sempurna.
Darragh Enright
3
Ini tampaknya memberi orangtua direktori tempat skrip dijalankan, bukan dari lokasi skrip.
Coquelicot
10
os.path.realpath('..')memberi Anda direktori induk dari direktori kerja saat ini . Itu biasanya bukan yang Anda inginkan.
Martijn Pieters
1
@DarraghEnright: Itu hanya terjadi di lingkungan pengemasan Python-script-to-exe. Itu salah satu pengecualian langka di mana mengandalkan dir bekerja saat ini akan menjadi alternatif.
Martijn Pieters
52

Seperti disebutkan dalam jawaban yang diterima

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, '/relative/path/to/file/you/want')

Saya hanya ingin menambahkan itu

string yang terakhir tidak dapat dimulai dengan garis miring terbalik, infact tidak ada string yang harus menyertakan garis miring terbalik

Seharusnya seperti itu

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, 'relative','path','to','file','you','want')

Jawaban yang diterima dapat menyesatkan dalam beberapa kasus, silakan lihat tautan ini untuk detailnya

Ahmed
sumber
4
Ya menggunakan os.path.joinlebih baik karena bergabung dengan mereka dengan pemisah khusus OS.
Farshid T
'/relative/path...'bukan jalan relatif. Apakah itu disengaja?
steveire
Jawaban ini sekarang kedaluwarsa, karena jawaban teratas telah diedit untuk menggunakan jalur relatif yang tepat di os.path.join(). Yang tersisa adalah preferensi untuk menggunakan string yang terpisah untuk setiap elemen path daripada hardcoding pemisah path.
Martijn Pieters
@ MartijnPieters Ya, jawaban teratas telah diedit agar sesuai dengan ini di bagian, tetapi string terpisah bukan preferensi - memisahkan sengatan seperti ini membuatnya os-independent.
jshrimp29
26

Sekarang tahun 2018, dan Python telah berevolusi sejak __future__lama. Jadi bagaimana menggunakan menakjubkan pathlibdatang dengan Python 3.4 untuk menyelesaikan tugas bukannya berjuang dengan os, os.path, glob, shutil, dll

Jadi kami memiliki 3 jalur di sini (mungkin digandakan):

  • mod_path: yang merupakan jalur skrip pembantu sederhana
  • src_path: yang berisi beberapa file templat yang menunggu untuk disalin.
  • cwd: direktori saat ini , tujuan dari file templat tersebut.

dan masalahnya adalah: kita tidak memiliki path lengkap src_path, hanya tahu itu path relatif ke mod_path.

Sekarang mari kita selesaikan ini dengan yang menakjubkan pathlib:

# Hope you don't be imprisoned by legacy Python code :)
from pathlib import Path

# `cwd`: current directory is straightforward
cwd = Path.cwd()

# `mod_path`: According to the accepted answer and combine with future power
# if we are in the `helper_script.py`
mod_path = Path(__file__).parent
# OR if we are `import helper_script`
mod_path = Path(helper_script.__file__).parent

# `src_path`: with the future power, it's just so straightforward
relative_path_1 = 'same/parent/with/helper/script/'
relative_path_2 = '../../or/any/level/up/'
src_path_1 = (mod_path / relative_path_1).resolve()
src_path_2 = (mod_path / relative_path_2).resolve()

Di masa depan, sesederhana itu. : D


Selain itu, kami dapat memilih dan memeriksa dan menyalin / memindahkan file templat tersebut dengan pathlib:

if src_path != cwd:
    # When we have different types of files in the `src_path`
    for template_path in src_path.glob('*.ini'):
        fname = template_path.name
        target = cwd / fname
        if not target.exists():
            # This is the COPY action
            with target.open(mode='wb') as fd:
                fd.write(template_path.read_bytes())
            # If we want MOVE action, we could use:
            # template_path.replace(target)
YaOzI
sumber
14

Pertimbangkan kode saya:

import os


def readFile(filename):
    filehandle = open(filename)
    print filehandle.read()
    filehandle.close()



fileDir = os.path.dirname(os.path.realpath('__file__'))
print fileDir

#For accessing the file in the same folder
filename = "same.txt"
readFile(filename)

#For accessing the file in a folder contained in the current folder
filename = os.path.join(fileDir, 'Folder1.1/same.txt')
readFile(filename)

#For accessing the file in the parent folder of the current folder
filename = os.path.join(fileDir, '../same.txt')
readFile(filename)

#For accessing the file inside a sibling folder.
filename = os.path.join(fileDir, '../Folder2/same.txt')
filename = os.path.abspath(os.path.realpath(filename))
print filename
readFile(filename)
Fahad Haleem
sumber
Ketika saya menjalankan ini di windows, saya menerima kesalahan: FileNotFoundError: [Errno 2] Tidak ada file atau direktori tersebut: '<path>' di mana <path> memiliki segmen jalur yang benar tetapi menggunakan \\ untuk pemisah.
lonstar
11

Lihat sys.path Seperti diinisialisasi pada startup program, item pertama dari daftar ini, path [0], adalah direktori yang berisi skrip yang digunakan untuk memanggil juru bahasa Python.

Gunakan jalur ini sebagai folder root tempat Anda menerapkan jalur relatif Anda

>>> import sys
>>> import os.path
>>> sys.path[0]
'C:\\Python25\\Lib\\idlelib'
>>> os.path.relpath(sys.path[0], "path_to_libs") # if you have python 2.6
>>> os.path.join(sys.path[0], "path_to_libs")
'C:\\Python25\\Lib\\idlelib\\path_to_libs'
Tom Leys
sumber
3
Itu belum tentu benar. Biasanya sys.path [0] adalah string atau titik kosong, yang merupakan jalur relatif ke direktori saat ini. Jika Anda ingin direktori saat ini, gunakan os.getcwd.
Jason Baker
Poster asli berkomentar bahwa direktori kerja saat ini adalah tempat yang salah untuk menjadi dasar jalur relatif. Anda benar dalam mengatakan bahwa sys.path [0] tidak selalu valid.
Tom Leys
Tidak, sys.path[0]tidak selalu disetel ke direktori induk. Kode python dapat dipanggil dengan -catau -mmelalui interpreter yang disematkan, pada titik mana sys.path[0]diatur ke sesuatu yang berbeda sama sekali.
Martijn Pieters
6

Alih-alih menggunakan

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

seperti pada jawaban yang diterima, akan lebih kuat untuk digunakan:

import inspect
import os
dirname = os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

karena menggunakan __file__ akan mengembalikan file dari mana modul dimuat, jika itu diambil dari file, jadi jika file dengan skrip dipanggil dari tempat lain, direktori yang dikembalikan tidak akan benar.

Jawaban ini memberikan detail lebih lanjut: https://stackoverflow.com/a/31867043/5542253 dan https://stackoverflow.com/a/50502/5542253

tersenyum
sumber
5
inspect.stack()adalah fungsi yang mahal untuk dipanggil. Ini mengambil info untuk semua frame stack, yang kemudian Anda buang dan hanya mendapatkan yang teratas untuk. Ini pada dasarnya memanggil inspect.getfile()objek modul, yang baru saja kembali module.__file__. Anda jauh lebih baik dari hanya menggunakan __file__.
Martijn Pieters
4

Hai pertama-tama Anda harus memahami fungsi os.path.abspath (path) dan os.path.relpath (path)

Singkatnya os.path.abspath (path) membuat jalur relatif ke jalur absolut . Dan jika jalur yang disediakan itu sendiri merupakan jalur absolut maka fungsinya mengembalikan jalur yang sama.

sama os.path.relpath (path) membuat path absolut ke path relatif . Dan jika jalur yang disediakan itu sendiri merupakan jalur relatif maka fungsinya mengembalikan jalur yang sama.

Contoh di bawah ini memungkinkan Anda memahami konsep di atas dengan benar :

misalkan saya punya file input_file_list.txt yang berisi daftar file input yang akan diproses oleh skrip python saya.

D: \ conc \ input1.dic

D: \ conc \ input2.dic

D: \ Copyioconc \ input_file_list.txt

Jika Anda melihat struktur folder di atas, input_file_list.txt ada di folder Copyofconc dan file yang akan diproses oleh skrip python ada di folder conc

Tetapi isi file input_file_list.txt adalah seperti yang ditunjukkan di bawah ini:

.. \ conc \ input1.dic

.. \ conc \ input2.dic

Dan skrip python saya hadir di D: drive.

Dan jalur relatif yang disediakan dalam file input_file_list.txt relatif terhadap jalur file input_file_list.txt .

Jadi ketika skrip python akan mengeksekusi direktori kerja saat ini (gunakan os.getcwd () untuk mendapatkan path)

Karena jalur relatif saya relatif ke input_file_list.txt , yaitu "D: \ Copyofconc" , saya harus mengubah direktori kerja saat ini menjadi "D: \ Copyofconc" .

Jadi saya harus menggunakan os.chdir ('D: \ Copyofconc') , jadi direktori kerja saat ini adalah "D: \ Copyofconc" .

Sekarang untuk mendapatkan file input1.dic dan input2.dic , saya akan membaca baris ".. \ conc \ input1.dic" maka akan menggunakan perintah

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (untuk mengubah path relatif ke path absolut. Di sini sebagai direktori kerja saat ini adalah "D: \ Copyofconc", file ". \ conc \ input1. dic "harus diakses relatif ke" D: \ Copyofconc ")

jadi input1_path akan menjadi "D: \ conc \ input1.dic"

Chandrajyoti Das
sumber
4

Kode ini akan mengembalikan jalur absolut ke skrip utama.

import os
def whereAmI():
    return os.path.dirname(os.path.realpath(__import__("__main__").__file__))

Ini akan berfungsi bahkan dalam modul.

BookOwl
sumber
Alih-alih mengimpor kembali, Anda akan menggunakan sys.modules['__main__'].__file__.
Martijn Pieters
3

Alternatif yang bekerja untuk saya:

this_dir = os.path.dirname(__file__) 
filename = os.path.realpath("{0}/relative/file.path".format(this_dir))
J0hnG4lt
sumber
0

Apa yang berhasil untuk saya gunakan sys.path.insert. Lalu saya menentukan direktori yang harus saya kunjungi. Misalnya saya hanya perlu naik satu direktori.

import sys
sys.path.insert(0, '../')
Kucing putih
sumber
1
Ini bergantung pada direktori kerja saat ini, yang bisa sangat berbeda dari apa yang sebenarnya Anda inginkan.
Martijn Pieters
-2

Saya tidak yakin apakah ini berlaku untuk beberapa versi yang lebih lama, tapi saya percaya Python 3.3 memiliki dukungan path relatif asli.

Misalnya kode berikut harus membuat file teks dalam folder yang sama dengan skrip python:

open("text_file_name.txt", "w+t")

(perhatikan bahwa tidak boleh ada garis depan atau garis miring terbalik di awal jika itu jalan relatif)

Samy Bencherif
sumber
benar, jadi ini akan bekerja dari CWD yang bukan apa yang diminta OP. Ingin bekerja dari lokasi skrip.
Samy Bencherif