Dlaczego os.path.join () nie działa w tym przypadku?


325

Poniższy kod nie zostanie dołączony, podczas debugowania polecenie nie przechowuje całej ścieżki, ale tylko ostatni wpis.

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

Kiedy to testuję, zapisuje tylko /new_sandbox/część kodu.

Odpowiedzi:


426

Te ostatnie ciągi nie powinny zaczynać się od ukośnika. Jeśli zaczynają się od ukośnika, są uważani za „ścieżkę absolutną” i wszystko przed nimi jest odrzucane.

Cytując dokumenty Pythona dlaos.path.join :

Jeśli komponent jest ścieżką bezwzględną, wszystkie poprzednie komponenty są odrzucane, a łączenie jest kontynuowane od komponentu ścieżki bezwzględnej.

Uwaga w systemie Windows zachowanie w stosunku do liter dysku, które wydaje się ulec zmianie w porównaniu do wcześniejszych wersji Pythona:

W systemie Windows litera dysku nie jest resetowana po r'\foo'napotkaniu komponentu ścieżki bezwzględnej (np. ). Jeśli komponent zawiera literę dysku, wszystkie poprzednie komponenty są wyrzucane, a litera dysku jest resetowana. Zauważ, że ponieważ istnieje bieżący katalog dla każdego dysku, os.path.join("c:", "foo")reprezentuje ścieżkę względem bieżącego katalogu na dysku C:( c:foo), a nie c:\foo.


85
-1: Żaden ciąg nie powinien zawierać znaku „/”. Jednym z punktów os.path.join jest zapobieganie umieszczaniu ukośników na ścieżce.
S.Lott,

6
Problem z str.join () polega oczywiście na tym, że nie eliminuje podwójnych ukośników. Myślę, że jest to główny cel dla osób używających os.path.join. np. „/” .join(['/etc/ ”,„ / conf ”]) powoduje trzy ukośniki:„ / etc /// conf ”
Dustin Rasener

17
@DustinRasener Możesz użyć, os.path.normpathaby osiągnąć ten cel.
Gareth Latty

5
nie ma pojęcia, dlaczego ludzie są sfrustrowani zachowaniem os.path.join. W innych językach równoważna biblioteka / metoda łączenia ścieżek zachowuje się dokładnie tak samo. Jest bezpieczniejszy i ma większy sens.
Don Cheadle,

19
Jest to frustrujące, ponieważ jest to ukryta magia , w przeciwieństwie do kardynalnej heurystyki „jawne jest lepsze niż niejawne”. I tak jest . Projektanci języków mogą wierzyć, że wiedzą lepiej, ale istnieją oczywiste i wyraźnie bezpieczne powody, aby od czasu do czasu chcieć to zrobić. Teraz nie możemy. Dlatego nie możemy mieć dobrych rzeczy.
Cecil Curry

151

Chodzi o os.path.join()to, aby uczynić swój program wieloplatformowym (Linux / Windows / etc).

Nawet jedno cięcie go rujnuje.

Ma to więc sens tylko wtedy, gdy jest używany z jakimś punktem odniesienia, takim jak os.environ['HOME']lub os.path.dirname(__file__).


75

os.path.join()może być używany w połączeniu z os.path.sepdo tworzenia ścieżki bezwzględnej, a nie względnej.

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

8
Zastosowanie os.path.sepjako pierwszego elementu do zbudowania absolutnej ścieżki jest lepsze niż jakakolwiek inna odpowiedź tutaj! Istotą używania os.pathraczej niż podstawowych metod str jest unikanie pisania /. Świetne jest także umieszczenie każdego podkatalogu jako nowego argumentu i usunięcie wszystkich ukośników. Prawdopodobnie dobrym pomysłem byłoby upewnienie się, że czek todaystrnie zaczyna się od ukośnika! ;)
drzemka92

3
Działa to również w systemie Windows (python 2.7.6). Nie wchodził w interakcje z „C: \” i dołączył do podkatalogów.
rickfoosusa


21

Aby zrozumieć, dlaczego to zaskakujące zachowanie nie jest całkowicie okropne, rozważ aplikację, która akceptuje nazwę pliku konfiguracyjnego jako argument:

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

Jeśli aplikacja jest wykonywana za pomocą:

$ myapp foo.conf

Plik konfiguracyjny /etc/myapp.conf/foo.confzostanie użyty.

Ale zastanów się, co się stanie, jeśli aplikacja zostanie wywołana z:

$ myapp /some/path/bar.conf

Następnie myapp należy użyć pliku konfiguracyjnego w /some/path/bar.conf(i nie /etc/myapp.conf/some/path/bar.conflub podobnie).

To może nie być świetne, ale uważam, że jest to motywacja do zachowania absolutnej ścieżki.


Dzięki! Zawsze nienawidziłem tego zachowania, dopóki nie przeczytałem twojej odpowiedzi! Jest to udokumentowane w docs.python.org/3.5/library/os.path.html#os.path.join , ale nie jest to uzasadnieniem .
Eli_B,

W tym momencie, gdy potrzebujesz dokładnie rozwiązania, wiele osób uważa za okropne.
ashrasmun

12

To dlatego, że '/new_sandbox/'zaczyna się od a, /a zatem zakłada się, że jest względny w stosunku do katalogu głównego. Usuń wiodące /.


8

Aby uczynić twoją funkcję bardziej przenośną, użyj jej jako takiej:

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

lub

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

8

Wypróbuj kombinację ciągów split("/")i *dla istniejących połączeń.

import os

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

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


Jak to działa...

split("/") zamienia istniejącą ścieżkę na listę: ['', 'home', 'build', 'test', 'sandboxes', '']

* przed listą wyodrębnia każdy element listy swój własny parametr


3

Spróbuj new_sandboxtylko

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

2

zrób to w ten sposób, bez dodatkowych cięć

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

0

Pamiętaj, że podobny problem może Cię ugryźć, jeśli użyjesz os.path.join()rozszerzenia, które już zawiera kropkę, co dzieje się automatycznie, gdy używasz os.path.splitext(). W tym przykładzie:

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

Chociaż extensionmoże się zdarzyć, że .jpgotrzymasz folder o nazwie „foobar” zamiast pliku o nazwie „foobar.jpg”. Aby temu zapobiec, musisz osobno dołączyć rozszerzenie:

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

0

można :strip'/'

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

0

Zalecam usunięcie drugiego i następujących ciągów os.path.sep, zapobiegając interpretacji ich jako ścieżek bezwzględnych:

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)

0
os.path.join("a", *"/b".split(os.sep))
'a/b'

pełniejsza wersja:

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

Co jeśli os.sep jest w rzeczywistości "\"? Wtedy pojawia się twój pierwszy przykład os.path.join("a", *"/b".split("\\")), który daje "/b"... Wątpię, żeby to był zamierzony rezultat.
NichtJens

1
Zaktualizowano - Przypuszczam, że musisz dać wskazówkę, ponieważ ścieżka, której używasz lokalnie, jest niezależna od systemu operacyjnego, na którym
biegasz

1
Tak. Alternatywnie można podzielić na obie często używane opcje ... ale wtedy inny system operacyjny może wymyślić trzeci.
NichtJens
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.