1
votes

Python: moyen élégant d'obtenir un itérable de tous les parents dans un chemin

En travaillant avec un système de ressources basé sur les chemins, l'application doit trouver la première ressource de travail qui gère une ressource donnée en fonction des chemins. J'ai besoin d'un moyen succinct et pythonique pour générer les éléments suivants:

Entrée:

/house/dogs/ralph/bone
/house/dogs/ralph
/house/dogs
/house

Sortie: p>

/house/dogs/ralph/bone

Remarque: Vous pouvez utiliser os.path ou des fonctions intégrées similaires, mais ce ne sont pas des ressources de système de fichiers. La sortie peut être tout ce qui est itérable (liste, ensemble, générateur, etc.).


3 commentaires

Pourquoi ne pouvez-vous pas diviser la chaîne sur / et prendre des tranches?


Je peux. Je recherche la meilleure façon (pythonique) de le faire. Ma première pensée est de diviser / joindre une fonction récursive, produire le résultat, mais il doit y avoir un meilleur moyen.


J'ai mis à jour ma réponse pour souligner la nature non-fichier des chemins posix purs


3 Réponses :


5
votes

Utilisez pathlib . PurePath propose un résumé interface avec des objets de type chemin qui n'ont aucune relation avec le système de fichiers. En particulier, PurePosixPath est le genre qui utilise des barres obliques ( / ) comme séparateurs:

def receding_path(p):
    p = PurePosixPath(p)
    while p != p.root:
        yield str(p)
        p = p.parent

for item in receding_path('/house/dogs/ralph/bone'):
    # do stuff to each item

Vous pouvez facilement boucler ceci:

p = PurePosixPath(...)
while p != p.root:
    # Do stuff to p
    p = p.parent

Une touche finale assez pythonique serait d'en faire un générateur:

>>> from pathlib import PurePosixPath
>>> p = PurePosixPath('/house/dogs/ralph/bone')
>>> str(p.parent)
/house/dogs/ralph
>>> str(p.parent.parent)
/house/dogs


1 commentaires

pathlib fournit également la propriété path.parents sur les objets Path . Définition issue de la documentation: une séquence immuable donnant accès aux ancêtres logiques du chemin.



1
votes

Une façon serait de diviser la chaîne sur "/" et de prendre des tranches successives.

out_strings = list(filter(None, ("/".join(s[:i]) for i in range(len(s), 0, -1))))
print(out_strings)
#['/house/dogs/ralph/bone',
# '/house/dogs/ralph',
# '/house/dogs',
# '/house']

Le filtre (Aucun, ...) est utilisé pour supprimer les chaînes vides.

Ou inverser la plage si vous voulez la sortie dans l'ordre que vous avez spécifié dans votre message:

in_string = "/house/dogs/ralph/bone"
s = in_string.split("/")
out_strings = list(filter(None, ("/".join(s[:i+1]) for i in range(len(s)))))
print(out_strings)
#['/house', '/house/dogs', '/house/dogs/ralph', '/house/dogs/ralph/bone']

p>


2 commentaires

Curieux: y a-t-il une raison pour laquelle vous filtrez au lieu de simplement référencer [: -1] puisque seul le dernier élément est vide?


@Bobby le délimiteur peut apparaître au début ou à la fin de la chaîne dans un chemin. Après le split , cela provoque des chaînes vides. En utilisant le filtre , je n'ai pas à déterminer quel index exclure, et il maintient le générateur (qui ne peut pas être indexé).



1
votes

Quelque chose d'une combinaison des deux réponses précédentes:

import pathlib
import os
def resources(path):
  parts = pathlib.Path(path).parts
  for n in range(len(parts), 1, -1):
    yield os.path.join(*parts[:n])


6 commentaires

Vous pouvez utiliser '/'.join au lieu de os.path.join pour le rendre indépendant du système d'exploitation. Alors peut-être enlever le premier élément?


J'ai l'impression que os.path.join est indépendant du système d'exploitation, et que l'utilisation de '/' le rendrait dépendant du système d'exploitation (fonctionne uniquement sur les systèmes Unix). os.path.join fait également ce qu'il faut avec l'élément de niveau supérieur automatiquement.


@JeffreyFroman, je crois que ce que @MadPhysicist dit, c'est que si vous utilisez os.path.join , le résultat sera différent si vous n'êtes pas sur un système * nix. L'indépendance du système d'exploitation empêchera votre solution de fonctionner sous Linux.


Bon, je comprends et que @Bobby: os.path.join utilisera le système d'exploitation. séparateur de chemin pour ce système d'exploitation. Si nous avons affaire à des chaînes qui sont des chemins de système de fichiers, alors probablement ces chaînes incluront le séparateur approprié à ce système d'exploitation. Si les chaînes ne sont pas des chemins de système de fichiers pour le système d'exploitation sur lequel nous allons fonctionner, alors pathlib ne fonctionnera pas du tout et nous devons opérer sur des chaînes et des caractères bruts en tant que tels. Nous sommes d'accord sur le fonctionnement de os.path.join - si nous allons utiliser pathlib, nous supposons déjà que nous utilisons un chemin approprié au système d'exploitation sur lequel nous fonctionnons .


@Policier. L'opposé. Il fonctionnera comme vous le souhaitez sous Linux, mais pas sous Windows. OP recherche un chemin séparé par barre oblique, sur tous les systèmes.


En regardant les nouvelles réponses impliquant PurePosixPath - et les modifications apportées à l'OP concernant ceux-ci n'étant PAS des chemins de système de fichiers - on dirait que cela utilisera également `` / '' sur Windows, donc je suppose que cela fonctionnerait ... mais comme mentionné Je n'ai pas de machine Windows à tester.