Библиотека, которая используется для построения деревьев, рассчитана только на неизменяемые файловые структуры. Другими словами, уже после создания ее поменять нельзя. Вместо этого можно сделать новую структуру на основе старой, в которой какие-то части будут изменены.
В этом уроке мы изучим неизменяемую структуру. Именно она выбрана для этого курса неслучайно. Такую структуру легче отлаживать и меньше шансов допустить ошибки. И она позволяет максимально погрузиться в использование функций высшего порядка.
Базовые операции с узлами
Пакет 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'}
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.