Python: 5 способов получить список файлов в директории, сравнение

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Новички в программировании на Python, желающие освоить работу с файлами
  • Опытные разработчики, стремящиеся оптимизировать производительность своих Python-проектов
  • Студенты и специалисты, обучающиеся или работающие в области программирования и анализа данных необходимыми знаниями по файловым операциям

    Работа с файлами — фундаментальный навык Python-разработчика. Будь вы новичок, создающий свой первый скрипт, или опытный программист, оптимизирующий производительность крупного проекта — рано или поздно вам понадобится получить список файлов в директории. Кажется простой задачей? Однако Python предлагает минимум пять разных способов её решения, каждый со своими нюансами и преимуществами. Давайте разберёмся, какой подход подойдёт именно для вашей задачи. 💡

Осваиваете работу с файлами и директориями, но хотите большего? Обучение Python-разработке от Skypro — это не просто теория. Вы создадите реальные проекты с нуля до деплоя, научитесь работать с файловой системой на профессиональном уровне и освоите все методы, описанные в этой статье, под руководством практикующих разработчиков. Инвестируйте в навыки, которые действительно востребованы на рынке!

Задача получения списка файлов в директории на Python

Получение списка файлов в директории — одна из базовых операций при работе с файловой системой. Эта операция требуется во множестве сценариев: от простой обработки группы файлов до создания сложных систем индексации или мониторинга файловой системы.

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

  • os — базовый модуль для работы с операционной системой
  • os.path — модуль для работы с путями файловой системы
  • glob — модуль для поиска файлов по шаблонам
  • pathlib — объектно-ориентированный подход к работе с путями (Python 3.4+)
  • scandir/os.scandir — высокопроизводительный итератор по содержимому директории

Андрей Петров, Python-разработчик с 8-летним опытом

Столкнулся с проблемой при работе над проектом автоматизации тестирования. Нам нужно было обрабатывать тысячи логов в разных директориях. Первоначально мы использовали os.listdir(), но когда количество файлов перевалило за 50 тысяч, скрипт начал заметно тормозить.

После профилирования кода выяснилось, что узкое место — именно в получении списка файлов. Переход на os.scandir() дал прирост производительности почти в 2 раза. А когда мы переписали код с использованием pathlib, код стал не только быстрее, но и значительно чище — вместо множества вызовов os.path.join() и os.path.isfile() мы получили элегантное решение с методами объектов Path.

Вывод: не стоит недооценивать базовые операции. Даже такая простая задача, как получение списка файлов, может стать критичной для производительности при масштабировании.

Давайте рассмотрим каждый из этих способов на конкретных примерах. Для демонстрации будем использовать директорию проекта со следующей структурой:

project/
├── data/
│ ├── file1.csv
│ ├── file2.csv
│ └── report.txt
├── src/
│ ├── main.py
│ └── utils.py
└── README.md

Пошаговый план для смены профессии

Использование os.listdir() для работы с файлами

Самый старый и базовый способ получения списка файлов — использование функции os.listdir(). Эта функция возвращает список строк с именами всех файлов и директорий в указанном пути (исключая '.' и '..').

Вот простой пример использования:

Python
Скопировать код
import os

# Получаем список всех файлов и директорий
entries = os.listdir('project/data')
print(entries) # ['file1.csv', 'file2.csv', 'report.txt']

# Фильтруем только файлы (без директорий)
files = [entry for entry in entries if os.path.isfile(os.path.join('project/data', entry))]
print(files) # ['file1.csv', 'file2.csv', 'report.txt']

# Фильтруем по расширению
csv_files = [file for file in files if file.endswith('.csv')]
print(csv_files) # ['file1.csv', 'file2.csv']

Преимущества os.listdir():

  • Простота использования
  • Доступность во всех версиях Python
  • Минимальные зависимости (только стандартная библиотека)

Недостатки os.listdir():

  • Возвращает только имена файлов, без полных путей
  • Не различает файлы и директории (требуется дополнительная проверка)
  • Ниже производительность при работе с большими директориями по сравнению с более новыми методами
  • Менее удобен для рекурсивного обхода
Задача Решение с os.listdir()
Получить все файлы в директории [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))]
Получить все директории [f for f in os.listdir(dir_path) if os.path.isdir(os.path.join(dir_path, f))]
Файлы с определенным расширением [f for f in os.listdir(dir_path) if f.endswith('.txt')]
Полные пути к файлам [os.path.join(dir_path, f) for f in os.listdir(dir_path)]

os.listdir() — проверенный временем метод, который до сих пор широко используется, особенно в коде, который должен быть совместим со старыми версиями Python. Однако для более сложных сценариев или при необходимости оптимизации производительности стоит рассмотреть альтернативные подходы. 🔍

Модуль glob: поиск файлов по шаблону

Модуль glob — мощный инструмент для поиска файлов с использованием шаблонов в стиле Unix. Это особенно удобно, когда вам нужно найти файлы, соответствующие определенному шаблону имени или расширению.

Основная функция модуля — glob.glob(pattern), которая возвращает список путей, соответствующих указанному шаблону.

Python
Скопировать код
import glob

# Получить все CSV файлы в директории data
csv_files = glob.glob('project/data/*.csv')
print(csv_files) # ['project/data/file1.csv', 'project/data/file2.csv']

# Получить все файлы в директории data
all_files = glob.glob('project/data/*')
print(all_files) # ['project/data/file1.csv', 'project/data/file2.csv', 'project/data/report.txt']

# Получить файлы с именем, содержащим цифру
numbered_files = glob.glob('project/data/*[0-9]*')
print(numbered_files) # ['project/data/file1.csv', 'project/data/file2.csv']

# Рекурсивный поиск всех Python-файлов (Python 3.5+)
py_files = glob.glob('project/**/*.py', recursive=True)
print(py_files) # ['project/src/main.py', 'project/src/utils.py']

В шаблонах glob используются специальные символы:

  • * — соответствует любому количеству любых символов
  • ? — соответствует любому одному символу
  • [abc] — соответствует любому символу из указанных в скобках
  • [!abc] или [^abc] — соответствует любому символу, кроме указанных в скобках
  • ** — в режиме recursive=True соответствует любому количеству директорий

Мария Соколова, Data Scientist

При работе над проектом анализа медицинских данных мне приходилось обрабатывать огромные наборы файлов с результатами обследований. Файлы хранились в сложной иерархии директорий, организованной по датам, типам исследований и категориям пациентов.

Сначала я пыталась написать собственную систему обхода с использованием os.walk(), что привело к громоздкому коду с множеством вложенных циклов. Когда коллега предложил использовать glob с рекурсивным поиском, я была удивлена элегантностью решения.

Одна строка кода:

all_ekg_files = glob.glob('data/**/EKG/*.dat', recursive=True)

заменила около 15 строк сложного кода с проверками и фильтрацией.

Более того, производительность оказалась лучше, а логика стала прозрачнее. Теперь при необходимости изменить критерии отбора файлов мне достаточно отредактировать шаблон, а не переписывать логику обхода директорий.

Шаблон glob Что ищет Пример результата
'project/*.py' Python-файлы в корне проекта ['project/setup.py']
'project/data/*.csv' CSV-файлы в директории data ['project/data/file1.csv', 'project/data/file2.csv']
'project/*/*.py' Python-файлы в директориях первого уровня ['project/src/main.py', 'project/src/utils.py']
'project/**/*.py' Все Python-файлы в проекте (рекурсивно) ['project/src/main.py', 'project/src/utils.py', 'project/tests/test_main.py']
'project/data/file[0-9].csv' CSV-файлы с именем file и цифрой ['project/data/file1.csv', 'project/data/file2.csv']

Преимущества модуля glob:

  • Интуитивно понятный синтаксис шаблонов
  • Возвращает полные пути к файлам
  • Поддержка рекурсивного поиска (начиная с Python 3.5)
  • Идеален для поиска файлов по маске или расширению

Недостатки модуля glob:

  • Менее эффективен при работе с очень большими директориями
  • Не предоставляет дополнительных метаданных о файлах (размер, время модификации)
  • Рекурсивный поиск доступен только в Python 3.5+

Pathlib: современный подход к работе с файлами

Модуль pathlib, введенный в Python 3.4, представляет собой объектно-ориентированный подход к работе с путями файловой системы. Это более современный и элегантный способ взаимодействия с файлами и директориями.

В отличие от традиционных подходов, где путь представлен строкой, pathlib использует объекты Path, которые инкапсулируют поведение путей и предоставляют методы для работы с ними.

Python
Скопировать код
from pathlib import Path

# Создаем объект Path для директории
data_dir = Path('project/data')

# Получаем список всех файлов и директорий
entries = list(data_dir.iterdir())
print(entries) # [PosixPath('project/data/file1.csv'), PosixPath('project/data/file2.csv'), ...]

# Фильтруем только файлы
files = [entry for entry in entries if entry.is_file()]
print(files) # [PosixPath('project/data/file1.csv'), PosixPath('project/data/file2.csv'), ...]

# Фильтруем по расширению
csv_files = list(data_dir.glob('*.csv'))
print(csv_files) # [PosixPath('project/data/file1.csv'), PosixPath('project/data/file2.csv')]

# Рекурсивный поиск всех Python-файлов
project_dir = Path('project')
py_files = list(project_dir.glob('**/*.py'))
print(py_files) # [PosixPath('project/src/main.py'), PosixPath('project/src/utils.py')]

Модуль pathlib предлагает множество полезных методов для работы с путями:

  • Path.iterdir() — итератор по содержимому директории
  • Path.glob(pattern) — поиск файлов по шаблону в директории
  • Path.rglob(pattern) — рекурсивный поиск файлов по шаблону
  • Path.is_file(), Path.is_dir() — проверка типа
  • Path.suffix, Path.stem, Path.name — компоненты пути
  • Path.stat() — метаданные файла (размер, время модификации и т.д.)

Один из главных плюсов pathlib — возможность создавать пути с помощью оператора /:

Python
Скопировать код
from pathlib import Path

base_dir = Path('project')
data_file = base_dir / 'data' / 'file1.csv' # PosixPath('project/data/file1.csv')

Этот синтаксис намного чище и интуитивно понятнее, чем традиционный os.path.join().

Преимущества pathlib:

  • Объектно-ориентированный подход делает код более читаемым
  • Множество удобных методов для работы с путями
  • Интуитивный синтаксис создания путей с оператором /
  • Кроссплатформенность: автоматически использует правильные разделители путей
  • Интеграция с другими модулями: многие функции Python теперь принимают объекты Path

Недостатки pathlib:

  • Доступен только в Python 3.4+ (но есть backport для Python 2 — pathlib2)
  • Может быть избыточным для простых сценариев
  • Немного менее производительный при работе с очень большими директориями

os.scandir(): высокопроизводительный итератор

Функция os.scandir(), добавленная в Python 3.5, представляет собой высокопроизводительный итератор по содержимому директории. Это промежуточное решение между базовым os.listdir() и объектно-ориентированным pathlib.

Главное отличие os.scandir() от os.listdir() заключается в том, что он возвращает не просто список имен файлов, а итератор объектов DirEntry, которые содержат дополнительную информацию о файлах.

Python
Скопировать код
import os

# Получаем итератор по содержимому директории
with os.scandir('project/data') as entries:
# Превращаем итератор в список для вывода
entries_list = list(entries)
print(entries_list) # [<DirEntry 'file1.csv'>, <DirEntry 'file2.csv'>, <DirEntry 'report.txt'>]

# Фильтруем только файлы
files = [entry for entry in entries_list if entry.is_file()]
print(files) # [<DirEntry 'file1.csv'>, <DirEntry 'file2.csv'>, <DirEntry 'report.txt'>]

# Фильтруем по расширению
csv_files = [entry for entry in files if entry.name.endswith('.csv')]
print(csv_files) # [<DirEntry 'file1.csv'>, <DirEntry 'file2.csv'>]

# Получаем полные пути
full_paths = [entry.path for entry in csv_files]
print(full_paths) # ['project/data/file1.csv', 'project/data/file2.csv']

Объект DirEntry имеет следующие полезные атрибуты и методы:

  • name — имя файла или директории
  • path — полный путь
  • is_file(), is_dir() — проверка типа
  • stat() — метаданные файла (кэшируются для производительности)
  • inode() — номер индексного дескриптора

Производительность — главное преимущество os.scandir(). При работе с большими директориями он может быть в несколько раз быстрее, чем os.listdir(), особенно когда вам нужно проверять типы файлов или получать метаданные.

Для рекурсивного обхода директорий с использованием os.scandir() можно написать собственную функцию:

Python
Скопировать код
import os

def scandir_recursive(path):
"""Рекурсивно перебирает все файлы в директории и поддиректориях"""
files = []
with os.scandir(path) as entries:
for entry in entries:
if entry.is_file():
files.append(entry.path)
elif entry.is_dir():
files.extend(scandir_recursive(entry.path))
return files

all_files = scandir_recursive('project')
print(all_files) # ['project/data/file1.csv', 'project/data/file2.csv', 'project/src/main.py', ...]

Преимущества os.scandir():

  • Высокая производительность при работе с большими директориями
  • Эффективное кэширование метаданных файлов
  • Предоставляет больше информации, чем os.listdir()
  • Более простой API, чем pathlib для некоторых задач

Недостатки os.scandir():

  • Доступен только в Python 3.5+ (но есть backport scandir для Python 2)
  • Менее интуитивный API по сравнению с pathlib
  • Требует явного закрытия итератора (рекомендуется использование конструкции with)

os.walk(): рекурсивный обход директорий

Функция os.walk() специально предназначена для рекурсивного обхода дерева директорий. Она генерирует кортежи (dirpath, dirnames, filenames) для каждой директории в дереве.

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

Python
Скопировать код
import os

# Получаем все файлы рекурсивно
all_files = []
for dirpath, dirnames, filenames in os.walk('project'):
# dirpath – текущая директория
# dirnames – список поддиректорий в текущей директории
# filenames – список файлов в текущей директории
for filename in filenames:
full_path = os.path.join(dirpath, filename)
all_files.append(full_path)

print(all_files) # ['project/README.md', 'project/data/file1.csv', 'project/data/file2.csv', ...]

# Фильтруем только Python-файлы
py_files = [file for file in all_files if file.endswith('.py')]
print(py_files) # ['project/src/main.py', 'project/src/utils.py']

Одно из ключевых преимуществ os.walk() — возможность контролировать процесс обхода, модифицируя список dirnames во время итерации:

Python
Скопировать код
import os

# Игнорируем определенные директории
for dirpath, dirnames, filenames in os.walk('project'):
# Удаляем директории .git и __pycache__ из списка для обхода
if '.git' in dirnames:
dirnames.remove('.git') # Не будет обходить .git
if '__pycache__' in dirnames:
dirnames.remove('__pycache__') # Не будет обходить __pycache__

# Продолжаем обработку...
for filename in filenames:
print(os.path.join(dirpath, filename))

Преимущества os.walk():

  • Создан специально для рекурсивного обхода директорий
  • Позволяет контролировать процесс обхода
  • Доступен во всех версиях Python
  • Предоставляет чёткую структуру данных для каждого уровня директорий

Недостатки os.walk():

  • Более сложный API по сравнению с альтернативами
  • Не так эффективен, как os.scandir() при работе с большими директориями
  • Не предоставляет прямой доступ к метаданным файлов

Сравнение методов и рекомендации по выбору

Выбор оптимального метода для получения списка файлов зависит от конкретных требований вашей задачи, версии Python и личных предпочтений.

Метод Производительность Удобство Рекурсивность Мин. версия Python Лучший сценарий использования
os.listdir() Средняя Базовое Нет (нужен собственный код) Все Простые скрипты, совместимость со старыми версиями
glob.glob() Средняя Высокое Да (с Python 3.5+) Все (рекурсия с 3.5+) Поиск файлов по шаблону, фильтрация по расширению
pathlib Средняя Очень высокое Да 3.4+ Современные проекты, комплексная работа с путями
os.scandir() Высокая Среднее Нет (нужен собственный код) 3.5+ Производительность, большие директории
os.walk() Средняя Среднее Да Все Полный контроль над рекурсивным обходом

Рекомендации по выбору метода:

  • Используйте pathlib, если:
  • Вы работаете с Python 3.4+
  • Хотите чистый, современный и читаемый код
  • Выполняете множество операций с путями (не только получение списка файлов)

  • Используйте glob.glob(), если:
  • Вам нужно найти файлы по шаблону (например, по расширению)
  • Требуется простой рекурсивный поиск
  • Нужен интуитивно понятный синтаксис

  • Используйте os.scandir(), если:
  • Производительность критична
  • Вы работаете с очень большими директориями
  • Вам нужен доступ к метаданным файлов

  • Используйте os.walk(), если:
  • Требуется полный контроль над процессом рекурсивного обхода
  • Нужно выборочно игнорировать определенные директории
  • Необходима совместимость со старыми версиями Python

  • Используйте os.listdir(), если:
  • Требуется максимальная совместимость
  • Выполняется простая операция без особых требований
  • Производительность не критична

На практике часто имеет смысл комбинировать подходы. Например, использовать pathlib для основной работы с путями, но переходить на os.scandir() для операций, требующих максимальной производительности. 🚀

Выбор правильного инструмента для работы с файловой системой в Python — это больше чем вопрос личных предпочтений. Это вопрос соответствия задаче, требованиям производительности и удобства сопровождения кода. Начните с pathlib для большинства современных проектов, но имейте в арсенале знания о других методах для ситуаций, когда они действительно необходимы. Помните: хороший разработчик знает несколько способов решения задачи и умеет выбрать оптимальный для конкретных условий.

Загрузка...