Почему в этом случае 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 состоит в том, чтобы предотвратить попадание любых косых черт в путь.
S.Lott

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

17
@DustinRasener Вы можете использовать os.path.normpathдля достижения этой цели.
Гарет Латти

5
Понятия не имею, почему люди разочарованы поведением os.path.join. В других языках эквивалентная библиотека / метод пути соединения ведет себя точно так же. Это безопаснее и имеет больше смысла.
Дон Чидл

19
Это расстраивает, потому что это неявная магия , в отличие от кардинальной эвристики «Явное лучше, чем неявное». И это есть . Разработчики языка могут полагать, что они знают лучше, но существуют очевидные и наглядно безопасные причины, чтобы время от времени хотеть это делать. Теперь мы не можем. Вот почему у нас не может быть хороших вещей.
Сесил Карри

151

Идея os.path.join()состоит в том, чтобы сделать вашу программу кроссплатформенной (linux / windows / etc).

Даже один слэш разрушает его.

Так что это имеет смысл только при использовании с какой-то точкой отсчета, например 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


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.