Mengapa os.path.join () tidak berfungsi dalam kasus ini?

325

Kode di bawah ini tidak akan bergabung, ketika men-debug perintah tidak menyimpan seluruh path tetapi hanya entri terakhir.

os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/')

Ketika saya menguji ini hanya menyimpan /new_sandbox/bagian dari kode.

chrissygormley
sumber

Jawaban:

426

Senar terakhir tidak harus dimulai dengan garis miring. Jika mereka mulai dengan garis miring, maka itu dianggap sebagai "jalur absolut" dan segala sesuatu sebelum mereka dibuang.

Mengutip dokumen Python untukos.path.join :

Jika komponen adalah jalur absolut, semua komponen sebelumnya dibuang dan bergabung terus dari komponen jalur absolut.

Catatan di Windows, perilaku dalam kaitannya dengan huruf drive, yang tampaknya telah berubah dibandingkan dengan versi Python sebelumnya:

Pada Windows, huruf drive tidak diatur ulang ketika komponen jalur absolut (misalnya, r'\foo') ditemukan. Jika komponen berisi huruf drive, semua komponen sebelumnya dibuang dan huruf drive diatur ulang. Perhatikan bahwa karena ada direktori saat ini untuk setiap drive, os.path.join("c:", "foo")mewakili jalur relatif ke direktori saat ini di drive C:( c:foo), bukan c:\foo.

Craig McQueen
sumber
85
-1: Tidak ada string yang harus menyertakan "/". Satu poin utama dari os.path.join adalah untuk mencegah meletakkan garis miring di jalan.
S.Lott
6
Masalahnya dengan str.join (), tentu saja, itu tidak akan menghilangkan garis miring ganda. Saya pikir ini adalah tujuan utama orang yang menggunakan os.path.join. mis. '/'.join(['/etc/', '/ conf']) menghasilkan tiga garis miring: '/ etc /// conf'
Dustin Rasener
17
@DustinRasener Anda dapat menggunakan os.path.normpathuntuk mencapai tujuan itu.
Gareth Latty
5
tidak ada petunjuk mengapa orang frustrasi atas perilaku os.path.join. Dalam bahasa lain, path / join library / metode yang setara berperilaku sama persis. Ini lebih aman, dan lebih masuk akal.
Don Cheadle
19
Ini membuat frustrasi karena ini adalah sihir implisit , bertentangan dengan heuristik utama dari "Eksplisit lebih baik daripada implisit." Dan itu adalah . Desainer bahasa mungkin percaya mereka tahu lebih baik, tetapi ada alasan yang jelas dan aman untuk sesekali ingin melakukan ini. Sekarang kita tidak bisa. Inilah sebabnya mengapa kita tidak dapat memiliki hal-hal yang baik.
Cecil Curry
151

Idenya os.path.join()adalah untuk membuat program Anda cross-platform (linux / windows / etc).

Bahkan satu tebasan menghancurkannya.

Jadi itu hanya masuk akal ketika digunakan dengan semacam titik referensi seperti os.environ['HOME']atau os.path.dirname(__file__).

Antony Hatchkins
sumber
75

os.path.join()dapat digunakan bersamaan dengan os.path.sepuntuk membuat jalur absolut daripada relatif.

os.path.join(os.path.sep, 'home','build','test','sandboxes',todaystr,'new_sandbox')
Ghammond
sumber
8
Penggunaan os.path.sepsebagai elemen pertama untuk membangun jalur absolut lebih baik daripada jawaban lain di sini! Inti dari menggunakan os.pathdaripada metode str dasar adalah untuk menghindari menulis /. Menempatkan setiap subdirektori sebagai argumen baru dan menghapus semua garis miring juga bagus. Mungkin akan menjadi ide yang baik untuk memastikan dengan cek yang todaystrtidak dimulai dengan garis miring! ;)
snooze92
3
Ini juga berfungsi di windows (python 2.7.6). Itu tidak intefere dengan 'C: \' dan bergabung dengan subdirektori.
rickfoosusa
21

Untuk membantu memahami mengapa perilaku mengejutkan ini tidak sepenuhnya mengerikan, pertimbangkan aplikasi yang menerima nama file konfigurasi sebagai argumen:

config_root = "/etc/myapp.conf/"
file_name = os.path.join(config_root, sys.argv[1])

Jika aplikasi dijalankan dengan:

$ myapp foo.conf

File config /etc/myapp.conf/foo.confakan digunakan.

Tetapi pertimbangkan apa yang terjadi jika aplikasi dipanggil dengan:

$ myapp /some/path/bar.conf

Maka myapp harus menggunakan file konfigurasi at /some/path/bar.conf(dan tidak /etc/myapp.conf/some/path/bar.confatau serupa).

Ini mungkin tidak bagus, tetapi saya percaya ini adalah motivasi untuk perilaku jalur absolut.

David Wolever
sumber
Terima kasih! Saya selalu membenci perilaku ini sampai membaca jawaban Anda! Ini didokumentasikan dalam docs.python.org/3.5/library/os.path.html#os.path.join , tetapi bukan motivasi untuk itu.
Eli_B
Saat ini ketika Anda membutuhkan solusi yang tepat, banyak orang menganggapnya mengerikan.
ashrasmun
12

Itu karena Anda '/new_sandbox/'mulai dengan a /dan dengan demikian dianggap relatif terhadap direktori root. Hapus yang memimpin /.

Amber
sumber
8

Untuk membuat fungsi Anda lebih portabel, gunakan seperti ini:

os.path.join(os.sep, 'home', 'build', 'test', 'sandboxes', todaystr, 'new_sandbox')

atau

os.path.join(os.environ.get("HOME"), 'test', 'sandboxes', todaystr, 'new_sandbox')
NuclearPeon
sumber
8

Coba kombo split("/")dan *untuk string dengan gabungan yang ada.

import os

home = '/home/build/test/sandboxes/'
todaystr = '042118'
new = '/new_sandbox/'

os.path.join(*home.split("/"), todaystr, *new.split("/"))


Bagaimana itu bekerja...

split("/") mengubah jalur yang ada menjadi daftar: ['', 'home', 'build', 'test', 'sandboxes', '']

* di depan daftar pecah setiap item daftar parameternya sendiri

openwonk
sumber
3

Coba new_sandboxsaja

os.path.join('/home/build/test/sandboxes/', todaystr, 'new_sandbox')
KAMU
sumber
2

lakukan seperti ini, tanpa terlalu banyak garis miring

root="/home"
os.path.join(root,"build","test","sandboxes",todaystr,"new_sandbox")
ghostdog74
sumber
0

Perhatikan bahwa masalah serupa dapat menggigit Anda jika Anda menggunakan os.path.join()untuk menyertakan ekstensi yang sudah menyertakan titik, yang merupakan apa yang terjadi secara otomatis saat Anda menggunakan os.path.splitext(). Dalam contoh ini:

components = os.path.splitext(filename)
prefix = components[0]
extension = components[1]
return os.path.join("avatars", instance.username, prefix, extension)

Meskipun extensionmungkin .jpgAnda berakhir dengan folder bernama "foobar" daripada file bernama "foobar.jpg". Untuk mencegahnya, Anda perlu menambahkan ekstensi secara terpisah:

return os.path.join("avatars", instance.username, prefix) + extension
shacker
sumber
0

Anda dapat stripdengan '/':

>>> os.path.join('/home/build/test/sandboxes/', todaystr, '/new_sandbox/'.strip('/'))
'/home/build/test/sandboxes/04122019/new_sandbox'
suhailvs
sumber
0

Saya akan merekomendasikan untuk menghapus dari string kedua dan berikut string os.path.sep, mencegah mereka ditafsirkan sebagai jalur absolut:

first_path_str = '/home/build/test/sandboxes/'
original_other_path_to_append_ls = [todaystr, '/new_sandbox/']
other_path_to_append_ls = [
    i_path.strip(os.path.sep) for i_path in original_other_path_to_append_ls
]
output_path = os.path.join(first_path_str, *other_path_to_append_ls)
Igor Fobia
sumber
0
os.path.join("a", *"/b".split(os.sep))
'a/b'

versi yang lebih lengkap:

import os

def join (p, f, sep = os.sep):
    f = os.path.normpath(f)
    if p == "":
        return (f);
    else:
        p = os.path.normpath(p)
        return (os.path.join(p, *f.split(os.sep)))

def test (p, f, sep = os.sep):
    print("os.path.join({}, {}) => {}".format(p, f, os.path.join(p, f)))
    print("        join({}, {}) => {}".format(p, f, join(p, f, sep)))

if __name__ == "__main__":
    # /a/b/c for all
    test("\\a\\b", "\\c", "\\") # optionally pass in the sep you are using locally
    test("/a/b", "/c", "/")
    test("/a/b", "c")
    test("/a/b/", "c")
    test("", "/c")
    test("", "c")
Neil McGill
sumber
Bagaimana jika os.sep sebenarnya adalah "\"? Kemudian contoh pertama Anda menjadi os.path.join("a", *"/b".split("\\")), yang menghasilkan "/b"... Saya ragu itu adalah hasil yang dimaksudkan.
NichtJens
1
Diperbarui - Saya kira Anda harus memberikan petunjuk sebagai jalan yang Anda gunakan secara lokal terlepas dari os yang Anda jalankan
Neil McGill
1
Iya. Atau satu dapat dibagi pada kedua opsi yang umum digunakan ... tetapi kemudian beberapa OS lain dapat muncul dengan yang ketiga.
NichtJens