Ожидаемое ли это поведение с os.path.join()

#python

#python

Вопрос:

Пример1, где путь 2 начинается с ‘/’, приводит к /dir2/dir3/ (отсутствующему пути 1)

 path1='/Volumes/disk1/'
path2='/dir2/dir3/'
print os.path.join(path1,path2)
 

Пример2, где путь 2 НЕ начинается с ‘/’, приводит к правильному /Volumes/disk1/dir2/dir3/ :

 path1='/Volumes/disk1/'
path2='dir2/dir3/'
print os.path.join(path1,path2)
 

Вопрос: Я думал, что цель os.path.join() — позволить нам избежать дополнительной утомительной работы по проверке, указан ли путь к файлу mac, Windows или Linux: все это делает одна команда. Но теперь, если мне придется смотреть, path2 начинается или не начинается с ‘/’ (или »), это разрушает все мои надежды и приносит массу дополнительного кода… Каково решение? Я не хочу делать это уродство:

 if path2 and path2.replace('\','/')[1:] and path2.replace('\','/').startswith('/'):
    path2=path2[1:]
 

Ответ №1:

Чтобы работать без проблем с проверкой разделителей, вы должны начать без них или полностью удалить их перед переходом к os.path.join() . В приведенном ниже коде я показываю 3 способа, которыми вы можете это сделать (живой пример Ideone для игры).

Отдельные каталоги

 import os
print os.path.join('Volumes', 'disk1', 'dir2', 'dir3')
 

Разделите пути, затем соедините

 path1 = '/Volumes/disk1/'
path2 = '/dir2/dir3/'

import os
# this will convert to the same as above:
# i.e., os.path.join('Volumes', 'disk1', 'dir2', 'dir3')
print os.path.join(*(path1.split(os.sep)   path2.split(os.sep)))
 

Функция Custum Join

Используя приведенный выше код, вы можете написать пользовательский join() интерфейс, который работает как для строк с одним, так и с несколькими путями:

 def join(*paths):
    import os
    return os.path.join(*[part for path in paths for part in path.split(os.sep)])

path1 = '/Volumes/disk1/'
path2 = '/dir2/dir3/'

print join(path1, path2)
 

Вывод:

 'Volumes/disk1/dir2/dir3'
 

Комментарии:

1. Потрясающе! Спасибо, что поделились!

2. Я никогда не видел, чтобы начальный * символ использовался внутри области видимости (*()). Какова его цель в Python, кроме умножения?

3. os.path.join() ожидает параметры, разделенные запятыми, а не массив. Оператор * преобразует массив в значения, разделенные запятыми.

4. Другими словами join(*['dir1','dir2','dir3']) = join('dir1','dir2','dir3')

Ответ №2:

Прямая форма документации,

Разумно соедините один или несколько компонентов пути. Если какой-либо компонент является абсолютным путем, все предыдущие компоненты (в Windows, включая предыдущую букву диска, если она была) отбрасываются, и объединение продолжается.

Вы видите свое поведение, потому что вы передаете ему абсолютный путь (путь, начинающийся с a '/' ). Ваша программа должна уметь обрабатывать разницу между ними, и если это то, что генерирует пути, убедитесь, что оно, вероятно, создает абсолютный путь, когда вы этого хотите, и относительный, когда вы этого хотите.

Объяснение, почему это полезно

Рассмотрим следующее. У меня есть интерфейс командной строки, который просит пользователя указать путь для вывода файла. В моей документации я говорю следующее:

 path: Path to an output file. If relative, will be placed inside ~/Documents.
 

Теперь в моем коде все, что мне нужно сделать, это:

 out_path = os.path.join('~','Documents', path)
 

и теперь out_path всегда будет содержать правильный путь. Если пользователь укажет volume_1/output.txt , файл завершится ~/Documents/volume_1/output.text . Если они укажут /mnt/volume_1/output.text , это закончится /mnt/volume_1/output.text , потому что абсолютный путь переопределяет относительную часть, которую мы предоставляем по умолчанию.

Комментарии:

1. Другими словами, я должен следить за тем, чтобы все аргументы second, third и etc были «относительными» путями (не начинающимися с ‘/’).

2. Я имею в виду, вы не должны «наблюдать» за чем-либо. Если части вашего кода задан абсолютный путь, следует предположить, что он получил абсолютный путь. Другими словами, он должен делать именно то, что join делает. Вам решать генерировать правильные типы путей, если вы их генерируете. Если пользователь является тем, кто их предоставляет, вы должны предположить, что если они дают вам абсолютный путь, это на самом деле то, что они имели в виду, а если это не так, то это ошибка пользователя.

3. Я надеялся os.path.join() , что он будет более «интеллектуальным», когда придет время разумно объединить один или несколько компонентов пути. Это расстраивает, что они упомянули «разумно» в документах. По крайней мере, вводит в заблуждение.