Чому в цьому випадку не працює os.path.join ()?


325

Наведений нижче код не приєднається, коли налагоджена команда не зберігає весь шлях, а лише останній запис.

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

Коли я тестую це, він зберігає лише /new_sandbox/частину коду.

Відповіді:


426

Останні рядки не повинні починатися з косої риски. Якщо вони починають з косою рискою, то вони вважаються "абсолютною стежкою", і все перед ними відкидається.

Цитуючи документи Python дляos.path.join :

Якщо компонент є абсолютним шляхом, всі попередні компоненти викидаються і з'єднання триває з компонента абсолютного шляху.

Зауважте, що в Windows поведінка стосовно букв диска, яка, здається, змінилася порівняно з попередніми версіями Python:

У Windows букву диска не скидають, коли r'\foo'зустрічається абсолютний компонент шляху (наприклад, ). Якщо компонент містить літеру диска, всі попередні компоненти викидаються, а літера диска скидається. Зауважте, що оскільки існує поточний каталог для кожного диска, os.path.join("c:", "foo")являє собою шлях відносно поточного каталогу на диску C:( c:foo), а не c:\foo.


85
-1: Жоден рядок не повинен містити "/". Однією з цілей os.path.join є запобігання покладенню косої риски на шляху.
С.Лотт

6
Проблема str.join (), звичайно, полягає в тому, що це не усуне подвійні косої риски. Я думаю, що це головна мета для людей, які використовують os.path.join. напр., '/'.join(kazi'/etc/', '/ conf']) призводить до трьох нахилів: '/ тощо /// conf'
Дастін Разенер,

17
@DustinRasener Ви можете використовувати os.path.normpathдля досягнення цієї мети.
Гарет Летті

5
немає поняття, чому люди засмучені через поведінку os.path.join. В інших мовах еквівалентна бібліотека / метод приєднання шляхів поводиться точно так само. Це безпечніше і має більше сенсу.
Don Cheadle

19
Це засмучує, оскільки це неявна магія , всупереч кардинальному евристиці "Явне краще, ніж неявне". І воно є . Мовні дизайнери можуть вважати, що вони знають краще, але існують очевидні та напевно безпечні причини, що іноді хочуть це зробити. Тепер ми не можемо. Ось чому ми не можемо мати хороших речей.
Сесіль Карі

151

Ідея os.path.join()полягає в тому, щоб зробити вашу програму крос-платформою (linux / windows / тощо).

Навіть одна косою рискою її розорить.

Отже, це має сенс лише при використанні з якоюсь точкою відліку, як-от os.environ['HOME']або os.path.dirname(__file__).


75

os.path.join()може використовуватися спільно з os.path.sepдля створення абсолютного, а не відносного шляху.

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

8
Використання os.path.sepяк першого елемента для побудови абсолютного шляху краще, ніж будь-яка інша відповідь тут! Весь сенс використання, os.pathа не основних методів str, полягає у тому, щоб уникнути написання /. Розміщення кожного підкаталогу як нового аргументу та видалення всіх косої риски також чудово. Напевно, це було б хорошою ідеєю, щоб переконатися в тому, що чек todaystrне починається з косої риски! ;)
snooze92

3
Це працює і на Windows (python 2.7.6). Він не заважав "C: \" і приєднався до підкаталогів.
rickfoosusa

23

Не використовуйте прямі косої риски на початку компонентів шляху, за винятком випадків переходу до кореневого каталогу:

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

дивіться також: http://docs.python.org/library/os.path.html#os.path.join


21

Щоб зрозуміти, чому це дивне поведінка не зовсім страшне, розгляньте програму, яка приймає ім'я файлу конфігурації як аргумент:

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

Якщо заявка виконана за допомогою:

$ myapp foo.conf

Буде використаний конфігураційний файл /etc/myapp.conf/foo.conf.

Але подумайте, що станеться, якщо додаток викликається за допомогою:

$ myapp /some/path/bar.conf

Потім myapp слід скористатися конфігураційним файлом на /some/path/bar.conf(а не /etc/myapp.conf/some/path/bar.confчи подібний).

Це може бути не чудово, але я вважаю, що це мотивація абсолютної поведінки на шляху.


Дякую! Я завжди ненавидів таку поведінку, поки не прочитав вашу відповідь! Це задокументовано в docs.python.org/3.5/library/os.path.html#os.path.join , але не мотивація цього.
Eli_B

Цей момент, коли потрібно саме рішення, багато хто вважає жахливим.
ашрасмун

12

Це тому, що ваш '/new_sandbox/'починається з а, /і, таким чином, вважається, що він відповідає кореневому каталогу. Зніміть провідну /.


8

Щоб зробити вашу функцію більш портативною, використовуйте її як таку:

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

або

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

8

Спробуйте комбінувати split("/")і *для рядків із наявними приєднаннями.

import os

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

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


Як це працює...

split("/") перетворює існуючий шлях у список: ['', 'home', 'build', 'test', 'sandboxes', '']

* перед списком розбиває кожен елемент списку власний параметр


3

Спробуйте new_sandboxлише

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

2

зробіть це так, без зайвих нахилів

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

0

Зауважте, що аналогічна проблема може вас перекусити, якщо ви os.path.join()включите розширення, яке вже містить крапку, що автоматично відбувається під час використання os.path.splitext(). У цьому прикладі:

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

Незважаючи на те, що extensionви, можливо, у .jpgвас з'явиться папка під назвою "foobar", а не файл під назвою "foobar.jpg". Щоб цього не допустити, потрібно додати розширення окремо:

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

0

Ви можете :strip'/'

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

0

Я рекомендую зняти рядок із другого та наступних рядків os.path.sep, не даючи інтерпретувати їх як абсолютні шляхи:

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'

більш повна версія:

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

Що робити, якщо насправді є os.sep "\"? Тоді стає ваш перший приклад os.path.join("a", *"/b".split("\\")), який дає результат "/b"... Я сумніваюся, що це задуманий результат.
NichtJens

1
Оновлено - я маю на увазі, що вам доведеться натякнути, що шлях сеп, який ви використовуєте локально, не залежить від осі, на якій ви біжите
Ніл Макгілл

1
Так. Крім того, можна розділити обидві часто використовувані варіанти ... але тоді деякі інші ОС можуть придумати третю.
NichtJens
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.