Como obter todos os subdiretórios imediatos em Python


Respostas:


31

Fiz alguns testes de velocidade em várias funções para retornar o caminho completo para todos os subdiretórios atuais.

tl; dr: Sempre use scandir:

list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]

Bônus: Com scandirvocê, você também pode obter apenas nomes de pastas usando em f.namevez de f.path.

Isso (assim como todas as outras funções abaixo) não usará a classificação natural . Isso significa que os resultados serão classificados da seguinte forma: 1, 10, 2. Para obter a classificação natural (1, 2, 10), consulte https://stackoverflow.com/a/48030307/2441026




Resultados : scandiré: 3x mais rápido que walk, 32x mais rápido que listdir(com filtro), 35x mais rápido que Pathlibe 36x mais rápido que listdire 37x (!) Mais rápido que glob.

Scandir:           0.977
Walk:              3.011
Listdir (filter): 31.288
Pathlib:          34.075
Listdir:          35.501
Glob:             36.277

Testado com W7x64, Python 3.8.1. Pasta com 440 subpastas.
Caso você se pergunte se listdirpoderia ser acelerado não executando os.path.join () duas vezes, sim, mas a diferença é basicamente inexistente.

Código:

import os
import pathlib
import timeit
import glob

path = r"<example_path>"



def a():
    list_subfolders_with_paths = [f.path for f in os.scandir(path) if f.is_dir()]
    # print(len(list_subfolders_with_paths))


def b():
    list_subfolders_with_paths = [os.path.join(path, f) for f in os.listdir(path) if os.path.isdir(os.path.join(path, f))]
    # print(len(list_subfolders_with_paths))


def c():
    list_subfolders_with_paths = []
    for root, dirs, files in os.walk(path):
        for dir in dirs:
            list_subfolders_with_paths.append( os.path.join(root, dir) )
        break
    # print(len(list_subfolders_with_paths))


def d():
    list_subfolders_with_paths = glob.glob(path + '/*/')
    # print(len(list_subfolders_with_paths))


def e():
    list_subfolders_with_paths = list(filter(os.path.isdir, [os.path.join(path, f) for f in os.listdir(path)]))
    # print(len(list(list_subfolders_with_paths)))


def f():
    p = pathlib.Path(path)
    list_subfolders_with_paths = [x for x in p.iterdir() if x.is_dir()]
    # print(len(list_subfolders_with_paths))



print(f"Scandir:          {timeit.timeit(a, number=1000):.3f}")
print(f"Listdir:          {timeit.timeit(b, number=1000):.3f}")
print(f"Walk:             {timeit.timeit(c, number=1000):.3f}")
print(f"Glob:             {timeit.timeit(d, number=1000):.3f}")
print(f"Listdir (filter): {timeit.timeit(e, number=1000):.3f}")
print(f"Pathlib:          {timeit.timeit(f, number=1000):.3f}")

1
Só quero agradecer, estava realmente procurando por isso. Ótima análise.
Cing

225
import os
def get_immediate_subdirectories(a_dir):
    return [name for name in os.listdir(a_dir)
            if os.path.isdir(os.path.join(a_dir, name))]

76

Por que ninguém mencionou glob? globpermite usar a expansão de nome de caminho no estilo Unix, e é minha função funcionar para quase tudo que precisa encontrar mais de um nome de caminho. Isso torna muito fácil:

from glob import glob
paths = glob('*/')

Observe que globretornará o diretório com a barra final (como o unix faria), enquanto a maioria das pathsoluções baseadas omitirá a barra final.


3
Boa solução, simples e funciona. Para quem não quer a barra final, ele pode usar isso paths = [ p.replace('/', '') for p in glob('*/') ].
Evan Hu

5
Pode ser mais seguro simplesmente cortar o último caractere [p[:-1] for p in paths], pois esse método de substituição também substituirá as barras invertidas escapadas no nome do arquivo (não que sejam comuns).
Ari

3
Ainda mais seguro, use a tira ('/') para remover as barras finais. Desta forma, garante que você não cortar qualquer caracteres que não são barras
Eliezer Miron

8
Por construção, você tem a garantia de ter uma barra final (por isso não é mais seguro), mas acho que é mais legível. Você definitivamente deseja usar, em rstripvez de strip, já que o último transformará todos os caminhos totalmente qualificados em caminhos relativos.
ari

7
complemento ao comentário @ari para iniciantes em python, como I: strip('/')removerá o inicial e o final '/', rstrip('/')removerá apenas o final
Titou

35

Marque " Obtendo uma lista de todos os subdiretórios no diretório atual ".

Aqui está uma versão do Python 3:

import os

dir_list = next(os.walk('.'))[1]

print(dir_list)

2
Extremamente inteligente. Embora a eficiência não importe ( ... totalmente ), estou curioso para saber se essa ou a expressão do gerador baseado em glob (s.rstrip("/") for s in glob(parent_dir+"*/"))é mais eficiente em termos de tempo. Minha suspeita intuitiva é que uma solução stat()baseada em deve ser profundamente mais rápida do que o globbing do tipo shell. Infelizmente, eu não tenho vontade e realmente descubro. os.walk()timeit
Cecil Curry

3
Observe que isso retorna os nomes dos subdiretórios sem o nome do diretório pai prefixado.
Paul Chernoch 11/08/19

19
import os, os.path

Para obter subdiretórios imediatos (caminho completo) em um diretório:

def SubDirPath (d):
    return filter(os.path.isdir, [os.path.join(d,f) for f in os.listdir(d)])

Para obter o subdiretório mais recente (mais recente):

def LatestDirectory (d):
    return max(SubDirPath(d), key=os.path.getmtime)

Para obter uma lista , basta adicionar list( filter(...) ).
user136036 27/01

12

os.walk é seu amigo nesta situação.

Diretamente da documentação:

walk () gera os nomes de arquivo em uma árvore de diretórios, percorrendo a árvore de cima para baixo ou de baixo para cima. Para cada diretório na árvore enraizado no topo do diretório (incluindo o próprio topo), ele gera três tuplas (caminho de rota, nome de diretório, nome de arquivo).


1
Lembre-se de que se você deseja apenas os subdiretórios de primeiro nível, interrompa a iteração os.walk após o primeiro conjunto de valores de retorno.
yoyo

11

Este método faz tudo de uma só vez.

from glob import glob
subd = [s.rstrip("/") for s in glob(parent_dir+"*/")]

7

Usando o módulo FilePath do Twisted:

from twisted.python.filepath import FilePath

def subdirs(pathObj):
    for subpath in pathObj.walk():
        if subpath.isdir():
            yield subpath

if __name__ == '__main__':
    for subdir in subdirs(FilePath(".")):
        print "Subdirectory:", subdir

Como alguns comentaristas perguntaram quais são as vantagens de usar as bibliotecas do Twisted, vou além da pergunta original aqui.


alguma documentação aprimorada em uma ramificação que explica as vantagens do FilePath; você pode querer ler isso.

Mais especificamente neste exemplo: diferente da versão da biblioteca padrão, esta função pode ser implementada sem importações . A função "subdireta" é totalmente genérica, pois opera apenas com base em seu argumento. Para copiar e mover os arquivos usando a biblioteca padrão, você precisa depender do " open" builtin " listdir" ", talvez" isdir"ou" os.walk"ou" shutil.copy". Talvez " os.path.join" também. Sem mencionar o fato de que você precisa de uma sequência passada em um argumento para identificar o arquivo real. Vamos dar uma olhada na implementação completa que copiará o "index.tpl" de cada diretório para "index.html":

def copyTemplates(topdir):
    for subdir in subdirs(topdir):
        tpl = subdir.child("index.tpl")
        if tpl.exists():
            tpl.copyTo(subdir.child("index.html"))

A função "subdirs" acima pode funcionar em qualquer FilePathobjeto semelhante. O que significa, entre outras coisas, ZipPathobjetos. Infelizmente, agora ZipPathé somente leitura, mas pode ser estendido para oferecer suporte à gravação.

Você também pode passar seus próprios objetos para fins de teste. Para testar as APIs de uso do os.path sugeridas aqui, você precisa usar nomes importados e dependências implícitas e geralmente executar magia negra para que seus testes funcionem. Com o FilePath, você faz algo assim:

class MyFakePath:
    def child(self, name):
        "Return an appropriate child object"

    def walk(self):
        "Return an iterable of MyFakePath objects"

    def exists(self):
        "Return true or false, as appropriate to the test"

    def isdir(self):
        "Return true or false, as appropriate to the test"
...
subdirs(MyFakePath(...))

Como tenho pouca exposição ao Twisted, sempre recebo informações e exemplos adicionais; é bom ver essa resposta para isso. Dito isto, uma vez que essa abordagem parece exigir substancialmente mais trabalho do que usar os módulos python internos e uma instalação Twisted, existem vantagens em usar isso que você possa adicionar à resposta?
Jarret Hardie

1
A resposta de Glyph provavelmente foi inspirada pelo fato de o TwistedLore também usar arquivos .tpl.
Constantin

Bem, claramente não espero que a inquisição em espanhol :-) Presumi que "* .tpl" fosse uma referência genérica a alguma extensão abstrata que significa "modelo", e não um modelo Twisted específico (eu já vi .tpl usado em muitos afinal de contas). Bom saber.
Jarret Hardie

+1, portanto, para alternar para o possível ângulo torcido, embora eu ainda queira entender o que o objeto 'FilePath' do Twisted e a função 'walk ()' adicionam à API padrão.
Jarret Hardie

Pessoalmente, acho que "FilePath.walk () produz objetos de caminho" muito mais fácil de lembrar do que "os.walk produz 3 tuplas de diretórios, diretórios, arquivos". Mas há outros benefícios. O FilePath permite polimorfismo, o que significa que você pode percorrer outras coisas além dos sistemas de arquivos. Por exemplo, você pode passar um twisted.python.zippath.ZipArchive para a função 'subdirs' e obter um gerador de ZipPaths em vez de FilePaths; sua lógica não muda, mas seu aplicativo agora lida magicamente com arquivos zip. Se quiser testá-lo, basta fornecer um objeto, não é necessário gravar arquivos reais.
Glyph

4

Acabei de escrever um código para mover as máquinas virtuais vmware e acabei usando os.path e shutilrealizando a cópia de arquivos entre subdiretórios.

def copy_client_files (file_src, file_dst):
    for file in os.listdir(file_src):
            print "Copying file: %s" % file
            shutil.copy(os.path.join(file_src, file), os.path.join(file_dst, file))

Não é muito elegante, mas funciona.


1

Aqui está uma maneira:

import os
import shutil

def copy_over(path, from_name, to_name):
  for path, dirname, fnames in os.walk(path):
    for fname in fnames:
      if fname == from_name:
        shutil.copy(os.path.join(path, from_name), os.path.join(path, to_name))


copy_over('.', 'index.tpl', 'index.html')

-1: não funcionará, já que shutil.copy será copiado para o diretório atual, então você substituirá 'index.html' no diretório atual uma vez para cada 'index.tpl' encontrado na árvore de subdiretórios.
Nosklo 29/04/09

1

Eu tenho que mencionar o path.py biblioteca , que eu uso com muita frequência.

A busca dos subdiretórios imediatos se torna tão simples quanto isso:

my_dir.dirs()

O exemplo completo de trabalho é:

from path import Path

my_directory = Path("path/to/my/directory")

subdirs = my_directory.dirs()

NB: my_directory ainda pode ser manipulado como uma string, já que Path é uma subclasse de string, mas fornece vários métodos úteis para manipular caminhos


1
def get_folders_in_directories_recursively(directory, index=0):
    folder_list = list()
    parent_directory = directory

    for path, subdirs, _ in os.walk(directory):
        if not index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)
        elif path[len(parent_directory):].count('/') + 1 == index:
            for sdirs in subdirs:
                folder_path = "{}/{}".format(path, sdirs)
                folder_list.append(folder_path)

    return folder_list

A seguinte função pode ser chamada como:

get_folders_in_directories_recursively (diretório, índice = 1) -> fornece a lista de pastas no primeiro nível

get_folders_in_directories_recursively (directory) -> fornece todas as subpastas


fazendo bom, versão python 3.6, mas eu precisava apagar "self", de dentro das variáveis ​​de função
locometro 25/06

1
estava usando dentro de uma classe, atualizei
Kanish Mathew 25/06

0
import glob
import os

def child_dirs(path):
     cd = os.getcwd()        # save the current working directory
     os.chdir(path)          # change directory 
     dirs = glob.glob("*/")  # get all the subdirectories
     os.chdir(cd)            # change directory to the script original location
     return dirs

A child_dirsfunção pega um caminho de diretório e retorna uma lista dos subdiretórios imediatos nele.

dir
 |
  -- dir_1
  -- dir_2

child_dirs('dir') -> ['dir_1', 'dir_2']

0
import pathlib


def list_dir(dir):
    path = pathlib.Path(dir)
    dir = []
    try:
        for item in path.iterdir():
            if item.is_dir():
                dir.append(item)
        return dir
    except FileNotFoundError:
        print('Invalid directory')

0

Um liner usando pathlib:

list_subfolders_with_paths = [p for p in pathlib.Path(path).iterdir() if p.is_dir()]
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.