Por que os.path.join () não funciona neste caso?


325

O código abaixo não se juntará, quando depurado, o comando não armazena todo o caminho, mas apenas a última entrada.

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

Quando eu testo isso, ele armazena apenas a /new_sandbox/parte do código.

Respostas:


426

As últimas strings não devem começar com uma barra. Se eles começam com uma barra, são considerados um "caminho absoluto" e tudo antes deles é descartado.

Citando os documentosos.path.join do Python para :

Se um componente é um caminho absoluto, todos os componentes anteriores são descartados e a união continua a partir do componente do caminho absoluto.

Observe no Windows, o comportamento em relação às letras de unidade, que parece ter mudado em comparação com as versões anteriores do Python:

No Windows, a letra da unidade não é redefinida quando um componente de caminho absoluto (por exemplo, r'\foo') é encontrado. Se um componente contiver uma letra de unidade, todos os componentes anteriores serão descartados e a letra da unidade será redefinida. Observe que, como existe um diretório atual para cada unidade, os.path.join("c:", "foo")representa um caminho relativo ao diretório atual na unidade C:( c:foo), não c:\foo.


85
-1: nenhuma string deve incluir um "/". Um ponto importante do os.path.join é evitar colocar barras no caminho.
22410 S.Lott

6
O problema com str.join () é, obviamente, que não eliminará barras duplas. Eu acho que esse é o objetivo principal das pessoas que usam os.path.join. por exemplo, '/'.join(['/etc/', '/ conf']) resulta em três barras: '/ etc /// conf'
Dustin Rasener

17
@DustinRasener Você pode usar os.path.normpathpara atingir esse objetivo.
precisa

5
nenhuma pista por que as pessoas estão frustradas com o comportamento do os.path.join. Em outros idiomas, a biblioteca / método de junção de caminho equivalente se comporta exatamente da mesma maneira. É mais seguro e faz mais sentido.
Don Cheadle

19
Isso é frustrante porque é mágica implícita , ao contrário da heurística cardinal de "Explícito é melhor que implícito". E isso é . Os designers de idiomas podem acreditar que sabem melhor, mas existem razões óbvias e comprovadamente seguras para ocasionalmente querer fazer isso. Agora não podemos. É por isso que não podemos ter coisas boas.
22915 Cecil Curry

151

A idéia os.path.join()é tornar seu programa multiplataforma (linux / windows / etc).

Até uma barra arruina.

Portanto, só faz sentido quando usado com algum tipo de ponto de referência como os.environ['HOME']ou os.path.dirname(__file__).


75

os.path.join()pode ser usado em conjunto com os.path.seppara criar um caminho absoluto em vez de relativo.

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

8
O uso de os.path.sepcomo primeiro elemento para construir um caminho absoluto é melhor do que qualquer outra resposta aqui! O objetivo de usar os.pathmétodos str, em vez dos básicos, é evitar a escrita /. Colocar todos os subdiretórios como um novo argumento e remover todas as barras também é ótimo. Provavelmente, seria uma boa idéia garantir uma verificação que todaystrnão comece com uma barra! ;)
snooze92

3
Isso funciona também no Windows (python 2.7.6). Não interferiu com 'C: \' e ingressou nos subdiretórios.
Rickfoosusa


21

Para ajudar a entender por que esse comportamento surpreendente não é totalmente terrível, considere um aplicativo que aceite um nome de arquivo de configuração como argumento:

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

Se o aplicativo for executado com:

$ myapp foo.conf

O arquivo de configuração /etc/myapp.conf/foo.confserá usado.

Mas considere o que acontece se o aplicativo for chamado com:

$ myapp /some/path/bar.conf

Então myapp deve usar o arquivo de configuração em /some/path/bar.conf(e não /etc/myapp.conf/some/path/bar.confou similar).

Pode não ser ótimo, mas acredito que essa é a motivação para o comportamento do caminho absoluto.


Obrigado! Eu sempre odiei esse comportamento até ler sua resposta! Está documentado em docs.python.org/3.5/library/os.path.html#os.path.join , mas não a motivação para isso.
Eli_B

Neste momento em que você precisa exatamente da solução que muitas pessoas consideram terrível.
ashrasmun 4/09/19

12

É porque você '/new_sandbox/'começa com a /e, portanto, é assumido como sendo relativo ao diretório raiz. Remova a guia /.


8

Para tornar sua função mais portátil, use-a da seguinte maneira:

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

ou

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

8

Experimente a combinação de split("/")e *para cadeias de caracteres com junções existentes.

import os

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

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


Como funciona...

split("/") transforma o caminho existente em lista: ['', 'home', 'build', 'test', 'sandboxes', '']

* na frente da lista divide cada item da lista com seu próprio parâmetro


3

Tente new_sandboxapenas

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

2

faça assim, sem as barras extras

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

0

Observe que um problema semelhante pode incomodá-lo se você os.path.join()incluir uma extensão que já inclua um ponto, que é o que acontece automaticamente quando você o usa os.path.splitext(). Neste exemplo:

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

Mesmo que extensionpode ser .jpgque você acabar com uma pasta chamada "foobar", em vez de um arquivo chamado "foobar.jpg". Para evitar isso, você precisa anexar a extensão separadamente:

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

0

você pode stripa '/':

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

0

Eu recomendo remover da segunda e das seguintes seqüências de caracteres a string os.path.sep, impedindo que elas sejam interpretadas como caminhos absolutos:

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'

uma versão mais completa:

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

E se o os.sep for realmente "\"? Então seu primeiro exemplo se torna os.path.join("a", *"/b".split("\\")), o que produz "/b"... Duvido que seja o resultado pretendido.
NichtJens

1
Atualizado - suponho que você precise dar uma dica, pois o caminho que você está usando localmente é independente do sistema operacional em que está executando
Neil McGill

1
Sim. Como alternativa, pode-se dividir as duas opções mais usadas ... mas, em seguida, algum outro sistema operacional pode chegar a uma terceira.
NichtJens
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.