Как перечислить только каталоги верхнего уровня в Python?

132

Я хочу иметь возможность отображать только каталоги внутри какой-либо папки. Это означает, что мне не нужны перечисленные имена файлов и не нужны дополнительные подпапки.

Посмотрим, поможет ли пример. В текущем каталоге у нас есть:

>>> os.listdir(os.getcwd())
['cx_Oracle-doc', 'DLLs', 'Doc', 'include', 'Lib', 'libs', 'LICENSE.txt', 'mod_p
ython-wininst.log', 'NEWS.txt', 'pymssql-wininst.log', 'python.exe', 'pythonw.ex
e', 'README.txt', 'Removemod_python.exe', 'Removepymssql.exe', 'Scripts', 'tcl',
 'Tools', 'w9xpopen.exe']

Однако мне не нужно перечислять имена файлов. Мне также не нужны подпапки, такие как \ Lib \ curses. По сути, то, что я хочу, работает со следующим:

>>> for root, dirnames, filenames in os.walk('.'):
...     print dirnames
...     break
...
['cx_Oracle-doc', 'DLLs', 'Doc', 'include', 'Lib', 'libs', 'Scripts', 'tcl', 'Tools']

Однако мне интересно, есть ли более простой способ достичь тех же результатов. У меня сложилось впечатление, что использование os.walk только для возврата на верхний уровень неэффективно / слишком много.

fuentesjr
источник

Ответы:

126

Отфильтруйте результат с помощью os.path.isdir () (и используйте os.path.join (), чтобы получить реальный путь):

>>> [ name for name in os.listdir(thedir) if os.path.isdir(os.path.join(thedir, name)) ]
['ctypes', 'distutils', 'encodings', 'lib-tk', 'config', 'idlelib', 'xml', 'bsddb', 'hotshot', 'logging', 'doc', 'test', 'compiler', 'curses', 'site-packages', 'email', 'sqlite3', 'lib-dynload', 'wsgiref', 'plat-linux2', 'plat-mac']
Томас Воутерс
источник
17
Это требует много обработки по сравнению с очень простой os.walk (). Next () [1]
Phyo Arkar Lwin
204

os.walk

Использование os.walkс nextфункцией элемента:

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

Для Python <= 2.5 используйте:

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

Как это работает

os.walkявляется генератором, и при вызове nextбудет получен первый результат в виде трех кортежей (dirpath, dirnames, filenames). Таким образом, [1]индекс возвращает только dirnamesиз этого кортежа.

Алекс Ковентри
источник
14
Еще немного описания: это генератор, он не будет ходить по другим каталогам, если вы ему не скажете. Итак .next () [1] в одной строке выполняет то же самое, что и все представления списка. Я бы, наверное, сделал что-то вроде, DIRNAMES=1а затем, next()[DIRNAMES]чтобы облегчить понимание для будущих разработчиков кода.
boatcoder
3
+1 потрясающее решение. Чтобы указать каталог для просмотра, используйте:os.walk( os.path.join(mypath,'.')).next()[1]
Дэниел Рейс,
42
для python v3: next (os.walk ('.')) [1]
Андре Соарес
если вы собираетесь делать больше, чем обработка текста; т.е. обработка в фактических папках, тогда могут потребоваться полные пути:sorted( [os.path.join(os.getcwd(), item) for item in os.walk(os.curdir).next()[1]] )
DevPlayer 03
52

Отфильтруйте список с помощью os.path.isdir для обнаружения каталогов.

filter(os.path.isdir, os.listdir(os.getcwd()))
Колин Дженсен
источник
5
Я думаю, что это, безусловно, лучшее сочетание удобочитаемости и краткости в любом из этих ответов.
vergenzt
20
Это не сработало. Я предполагаю, что это os.listdirвозвращает имя файла / папки, переданное os.path.isdir, но для последнего нужен полный путь.
Дэниел Рейс,
3
фильтр быстрее, чем os.walk timeit(os.walk(os.getcwd()).next()[1]) 1000 loops, best of 3: 734 µs per loop timeit(filter(os.path.isdir, os.listdir(os.getcwd()))) 1000 loops, best of 3: 477 µs per loop
Б.Кочис
14
directories=[d for d in os.listdir(os.getcwd()) if os.path.isdir(d)]
Марк Родди
источник
4
Это можно сократить до filter (os.path.isdir, os.listdir (os.getcwd ())
Джон Милликин,
3
Есть ли у кого-нибудь информация о том, быстрее ли фильтр или понимание списка? В противном случае это просто субъективный аргумент. Это, конечно, предполагает, что в cwd 10 миллионов каталогов и производительность является проблемой.
Марк Родди,
12

Обратите внимание, что вместо того, чтобы делать os.listdir(os.getcwd()), предпочтительнее делать os.listdir(os.path.curdir). На один вызов функции меньше, и он такой же переносимый.

Итак, чтобы завершить ответ, получить список каталогов в папке:

def listdirs(folder):
    return [d for d in os.listdir(folder) if os.path.isdir(os.path.join(folder, d))]

Если вы предпочитаете полные пути, используйте эту функцию:

def listdirs(folder):
    return [
        d for d in (os.path.join(folder, d1) for d1 in os.listdir(folder))
        if os.path.isdir(d)
    ]
tzot
источник
9

Кажется, это тоже работает (по крайней мере, на Linux):

import glob, os
glob.glob('*' + os.path.sep)
Travis
источник
1
+1 за glob. Это может сэкономить вам много кода, особенно итераций, и очень похоже на использование терминала UNIX ( ls)
Джерард,
5
Вместо glob.glob ('*' + os.path.sep) вы можете написать [dir вместо dir в glob.glob ("*"), если os.path.isdir (dir)]
Eamonn MR
8

Просто добавлю, что использование os.listdir () не требует «много обработки по сравнению с очень простой os.walk (). Next () [1]» . Это потому, что os.walk () внутренне использует os.listdir (). Фактически, если вы протестируете их вместе:

>>>> import timeit
>>>> timeit.timeit("os.walk('.').next()[1]", "import os", number=10000)
1.1215229034423828
>>>> timeit.timeit("[ name for name in os.listdir('.') if os.path.isdir(os.path.join('.', name)) ]", "import os", number=10000)
1.0592019557952881

Фильтрация os.listdir () выполняется немного быстрее.

Фос
источник
2
Далее в Python 3.5 является более быстрым способом получения содержимого каталога: python.org/dev/peps/pep-0471
Фос
1
pep-0471 - scandirпакет - доступен для Python 2.6 и более поздних версий как устанавливаемый пакет на PyPI. Он предлагает замену для os.walkи os.listdirчто гораздо быстрее.
Фос
6

Намного более простой и элегантный способ - использовать это:

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

Запустите этот скрипт в той же папке, для которой вы хотите получить имена папок. Он даст вам только точное имя папок (и это тоже без полного пути к папкам).

манты
источник
6

Используя понимание списка,

[a for a in os.listdir() if os.path.isdir(a)]

Я думаю это самый простой способ

KBLee
источник
2

будучи здесь новичком, я пока не могу напрямую комментировать, но вот небольшое исправление, которое я хотел бы добавить к следующей части ответа ΤΖΩΤΖΙΟΥ :

Если вы предпочитаете полные пути, используйте эту функцию:

def listdirs(folder):  
  return [
    d for d in (os.path.join(folder, d1) for d1 in os.listdir(folder))
    if os.path.isdir(d)
]

для тех, кто все еще использует python <2.4 : внутренняя конструкция должна быть списком, а не кортежем, и поэтому должна выглядеть так:

def listdirs(folder):  
  return [
    d for d in [os.path.join(folder, d1) for d1 in os.listdir(folder)]
    if os.path.isdir(d)
  ]

в противном случае возникает синтаксическая ошибка.

antiplex
источник
Я знаю, что это было давно, но этот первый пример мне очень помог.
Inbar Rose
1
Вы получаете синтаксическую ошибку, потому что ваша версия не поддерживает выражения генератора. Они были введены в Python 2.4, тогда как понимание списков было доступно с Python 2.0.
awatts
1
[x for x in os.listdir(somedir) if os.path.isdir(os.path.join(somedir, x))]
Моу
источник
1

Для списка полных имен путей я предпочитаю эту версию другим решениям здесь:

def listdirs(dir):
    return [os.path.join(os.path.join(dir, x)) for x in os.listdir(dir) 
        if os.path.isdir(os.path.join(dir, x))]
Малиус Арт
источник
1
scanDir = "abc"
directories = [d for d in os.listdir(scanDir) if os.path.isdir(os.path.join(os.path.abspath(scanDir), d))]
ПНВ
источник
0

Более безопасный вариант, который не сработает, когда нет каталога.

def listdirs(folder):
    if os.path.exists(folder):
         return [d for d in os.listdir(folder) if os.path.isdir(os.path.join(folder, d))]
    else:
         return []
Алексей Гаврилов
источник
0

Вот так?

>>>> [path for path in os.listdir(os.getcwd()) if os.path.isdir(path)]
Кирк Штраузер
источник
0

Python 3.4 вводится в pathlibмодуле в стандартную библиотеку, которая обеспечивает объектно - ориентированный подход для обработки файловой системы путей:

from pathlib import Path

p = Path('./')
[f for f in p.iterdir() if f.is_dir()]
joelostblom
источник
-1
-- This will exclude files and traverse through 1 level of sub folders in the root

def list_files(dir):
    List = []
    filterstr = ' '
    for root, dirs, files in os.walk(dir, topdown = True):
        #r.append(root)
        if (root == dir):
            pass
        elif filterstr in root:
            #filterstr = ' '
            pass
        else:
            filterstr = root
            #print(root)
            for name in files:
                print(root)
                print(dirs)
                List.append(os.path.join(root,name))
            #print(os.path.join(root,name),"\n")
                print(List,"\n")

    return List
Венката Маддинени
источник