Зарегистрируйтесь, чтобы продолжить обучение

Манипуляции с виртуальной файловой системой Python: Деревья

Библиотека, которая используется для построения деревьев, рассчитана только на неизменяемые файловые структуры. Другими словами, уже после создания ее поменять нельзя. Вместо этого можно сделать новую структуру на основе старой, в которой какие-то части будут изменены.

В этом уроке мы изучим неизменяемую структуру. Именно она выбрана для этого курса неслучайно. Такую структуру легче отлаживать и меньше шансов допустить ошибки. И она позволяет максимально погрузиться в использование функций высшего порядка.

Базовые операции с узлами

Пакет python-immutable-fs-trees позволяет не только создавать, но и извлекать данные из уже созданных файлов и директорий. Они позволяют не лезть во внутреннюю структуру самого дерева:

from hexlet import fs
tree = fs.mkdir('/', [fs.mkfile('hexlet.log')], {'hidden': True})
fs.get_name(tree)
# '/'
fs.get_meta(tree).get('hidden')
# True
[file] = fs.get_children(tree)
fs.get_name(file)
# 'hexlet.log'
fs.get_meta(file).get('unknown')

# А вот так делать не надо
# У файлов нет потомков
fs.get_children(file)

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

from hexlet import fs
tree = fs.mkdir('/', [fs.mkfile('hexlet.log')], {'hidden': True})
fs.is_directory(tree)
# True
fs.is_file(tree)
# False
[file] = fs.get_children(tree)
fs.is_file(file)
# True
fs.is_directory(file)
# False

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

Обработка

Любая обработка в неизменяемом стиле сводится к формированию новых данных на основе старых. Ниже мы реализуем некоторые варианты преобразования, раскрывающие эту идею.

Изменение имени файла

Фактически можно создать новый файл с метаданными старого:

from hexlet import fs
import copy

file = fs.mkfile('one', {'size': 35})
# При переименовании важно сохранить метаданные
new_meta = copy.deepcopy(fs.get_meta(file))
new_file = fs.mkfile('new name', new_meta)

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

Как только мы захотим внести изменения в новое, мы сломаем старое:

file = fs.mkfile('one', {'size': 35})
new_meta = fs.get_meta(file)
new_meta['size'] = 15
new_file = fs.mkfile('new name', new_meta)
fs.get_meta(new_file)
# {'size': 15}

# Бум! У file тоже поменялись метаданные
fs.get_meta(file)
# {'size': 15}

Сортировка содержимого директории

Также данные внутри директории можно отсортировать:

tree = fs.mkdir('/', [
    fs.mkfile('one'),
    fs.mkfile('two'),
    fs.mkdir('three'),
])

children = fs.get_children(tree)
new_meta = copy.deepcopy(fs.get_meta(tree))
# Reverse изменяет массив, поэтому клонируем
new_children = children[:]
# Делаем сортировку в обратном порядке, то есть разворачиваем список
new_children.reverse()
tree2 = fs.mkdir(fs.get_name(tree), new_children, new_meta)
list(map(fs.get_name, fs.get_children(tree2)))
# ['three', 'two', 'one']

Обновление содержимого директории

Еще мы можем обновить содержимое директории:

tree = fs.mkdir('/', [
    fs.mkfile('oNe'),
    fs.mkfile('Two'),
    fs.mkdir('THREE'),
])

# Приводим к нижнему регистру имена директорий и файлов внутри конкретной директории
def to_lower(node):
    name = fs.get_name(node)
    new_meta = copy.deepcopy(fs.get_meta(node))
    if fs.is_directory(node):
        return fs.mkdir(name.lower(), fs.get_children(node), new_meta)
    return fs.mkfile(name.lower(), new_meta)

children = fs.get_children(tree)
new_children = list(map(to_lower, children))
# Обязательно копируем метаданные
new_meta = copy.deepcopy(fs.get_meta(tree))
tree2 = fs.mkdir(fs.get_name(tree), new_children, new_meta)
list(map(fs.get_name, fs.get_children(tree2)))
# ['one', 'two', 'three']

Удаление файлов внутри директории

Кроме того, файлы можно удалять:

tree = fs.mkdir('/', [
    fs.mkfile('one'),
    fs.mkfile('two'),
    fs.mkdir('three'),
])

children = fs.get_children(tree)
new_children = list(filter(fs.is_directory, children))
new_meta = copy.deepcopy(fs.get_meta(tree))
fs.mkdir(fs.get_name(tree), new_children, new_meta)
# {'name': '/', 'children': [{'name': 'three', 'children': [], 'meta': {}, 'type': 'directory'}], 'meta': {}, 'type': 'directory'}

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
Программирование на Python, Разработка веб-приложений и сервисов используя Django, проектирование и реализация REST API
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»