Pourquoi os.path.join () ne fonctionne-t-il pas dans ce cas?


325

Le code ci-dessous ne se joindra pas, lors du débogage, la commande ne stocke pas le chemin complet mais uniquement la dernière entrée.

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

Lorsque je teste cela, il ne stocke que la /new_sandbox/partie du code.

Réponses:


426

Ces dernières chaînes ne doivent pas commencer par une barre oblique. S'ils commencent par une barre oblique, ils sont alors considérés comme un "chemin absolu" et tout ce qui se trouve devant eux est rejeté.

Citant les documents Python pouros.path.join :

Si un composant est un chemin absolu, tous les composants précédents sont supprimés et la jointure continue à partir du composant de chemin absolu.

Remarque sur Windows, le comportement par rapport aux lettres de lecteur, qui semble avoir changé par rapport aux versions antérieures de Python:

Sous Windows, la lettre de lecteur n'est pas réinitialisée lorsqu'un composant de chemin absolu (par exemple, r'\foo') est rencontré. Si un composant contient une lettre de lecteur, tous les composants précédents sont jetés et la lettre de lecteur est réinitialisée. Notez qu'étant donné qu'il existe un répertoire en cours pour chaque lecteur, il os.path.join("c:", "foo")représente un chemin relatif au répertoire en cours sur le lecteur C:( c:foo), non c:\foo.


85
-1: Aucune chaîne ne doit inclure de "/". Un point entier de os.path.join est d'empêcher de mettre des barres obliques dans le chemin.
S.Lott

6
Le problème avec str.join () est, bien sûr, qu'il n'éliminera pas les doubles barres obliques. Je pense que c'est le but principal pour les gens qui utilisent os.path.join. Par exemple, '/'.join(['/etc/', '/ conf']) entraîne trois barres obliques: '/ etc /// conf'
Dustin Rasener

17
@DustinRasener Vous pouvez utiliser os.path.normpathpour atteindre cet objectif.
Gareth Latty

5
aucune idée pourquoi les gens sont frustrés par le comportement os.path.join. Dans d'autres langues, la bibliothèque / méthode de jointure par chemin équivalent se comporte exactement de la même manière. C'est plus sûr et plus logique.
Don Cheadle

19
C'est frustrant car c'est de la magie implicite , contrairement à l' heuristique cardinale "Explicit vaut mieux qu'implicite". Et ça l' est . Les concepteurs de langage peuvent croire qu'ils savent mieux, mais il existe des raisons évidentes et manifestement sûres de vouloir parfois le faire. Maintenant, nous ne pouvons plus. C'est pourquoi nous ne pouvons pas avoir de bonnes choses.
Cecil Curry

151

L'idée os.path.join()est de rendre votre programme multi-plateforme (linux / windows / etc).

Même une barre oblique la ruine.

Cela n'a donc de sens que lorsqu'il est utilisé avec une sorte de point de référence comme os.environ['HOME']ou os.path.dirname(__file__).


75

os.path.join()peut être utilisé conjointement avec os.path.seppour créer un chemin absolu plutôt que relatif.

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

8
L'utilisation de os.path.sepcomme premier élément pour construire un chemin absolu est meilleure que toute autre réponse ici! L'intérêt d'utiliser os.pathdes méthodes str plutôt que de base est d'éviter d'écrire /. Placer chaque sous-répertoire en tant que nouvel argument et supprimer toutes les barres obliques est également excellent. Ce serait probablement une bonne idée de s'assurer avec un chèque qui todaystrne commence pas par une barre oblique! ;)
snooze92

3
Cela fonctionne également sur Windows (python 2.7.6). Il n'interfère pas avec 'C: \' et rejoint les sous-répertoires.
rickfoosusa


21

Pour aider à comprendre pourquoi ce comportement surprenant n'est pas entièrement terrible, considérez une application qui accepte un nom de fichier de configuration comme argument:

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

Si l'application est exécutée avec:

$ myapp foo.conf

Le fichier de configuration /etc/myapp.conf/foo.confsera utilisé.

Mais considérez ce qui se passe si l'application est appelée avec:

$ myapp /some/path/bar.conf

Ensuite myapp , utilisez le fichier de configuration à /some/path/bar.conf(et non /etc/myapp.conf/some/path/bar.confou similaire).

Ce n'est peut-être pas génial, mais je crois que c'est la motivation pour le comportement absolu du chemin.


Merci! J'avais toujours détesté ce comportement jusqu'à la lecture de votre réponse! C'est documenté dans docs.python.org/3.5/library/os.path.html#os.path.join , mais pas la motivation pour cela.
Eli_B

Ce moment où vous avez besoin exactement de la solution que beaucoup de gens jugent terrible.
ashrasmun

12

C'est parce que votre '/new_sandbox/'commence par un /et est donc supposé être relatif au répertoire racine. Retirez l'interligne /.


8

Pour rendre votre fonction plus portable, utilisez-la comme telle:

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

Essayez la combinaison de split("/")et *pour les chaînes avec des jointures existantes.

import os

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

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


Comment ça fonctionne...

split("/") transforme le chemin existant en liste: ['', 'home', 'build', 'test', 'sandboxes', '']

* devant la liste éclate chaque élément de la liste de son propre paramètre


3

Essayez avec new_sandboxseulement

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

2

faites comme ça, sans trop de barres obliques

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

0

Notez qu'un problème similaire peut vous mordre si vous utilisez os.path.join()pour inclure une extension qui inclut déjà un point, ce qui se produit automatiquement lorsque vous utilisez os.path.splitext(). Dans cet exemple:

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

Même si extensionpeut - être .jpgvous vous retrouvez avec un dossier nommé « toto » plutôt que d' un fichier appelé « foobar.jpg ». Pour éviter cela, vous devez ajouter l'extension séparément:

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

0

vous pouvez striple '/':

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

0

Je recommanderais de retirer de la deuxième chaîne et des chaînes suivantes la chaîne os.path.sep, en les empêchant d'être interprétés comme des chemins absolus:

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'

une version plus complète:

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

Et si os.sep l'est réellement "\"? Alors votre premier exemple devient os.path.join("a", *"/b".split("\\")), ce qui donne "/b"... Je doute que ce soit le résultat escompté.
NichtJens

1
Mise à jour - Je suppose que vous devez donner un indice car le chemin sep que vous utilisez localement indépendant de celui de l'OS que vous utilisez
Neil McGill

1
Oui. Alternativement, on pourrait diviser les deux options couramment utilisées ... mais un autre système d'exploitation pourrait en proposer un troisième.
NichtJens
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.