Язык программирования Python

Расширенное управление файлами в Python

При работе над задачами системного уровня необходимо регулярно применять одно и то же действие ко всем (Python) файлам одного типа: архивирование, подсчет количества строк, удаление файлов журнала старше 30 дней и т. д…

Просмотр записей в каталоге

listdir

Пакет ‘os’ предоставляет функцию ‘listdir’, которая для заданного имени каталога возвращает список имен файлов/каталогов, находящихся в нем.

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

from os import listdir for name in listdir("C:\\Temp"): print(name)
Code language: JavaScript (javascript)

walk

walk – это итератор, который позволяет пользователю избежать рекурсивного просмотра каталога.

from os import walk for root, dirs, files in walk('C:\\Temp', topdown=True): print(root) print(dirs) print(files) print('--------------------------------')
Code language: PHP (php)

Эта функция выделяет множество списков строк.

scandir

Начиная с Python 3.4, рекомендуемым методом является ‘scandir’, поскольку он гораздо более эффективен. Рост производительности может достигать 20 раз.

from os import scandir with scandir("C:\\Temp") as it: for entry in it: print(entry.name)
Code language: JavaScript (javascript)

Контекстуальное использование с ‘with’ является важным, оно гарантирует освобождение открытых ресурсов.

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

Повторное использование

Идея заключается в том, чтобы создать функцию ‘filemap’, которая будет рекурсивно просматривать дерево и применять действие ко всем файлам, которые являются целью.

Затем мы можем повторно использовать эту функцию в различных контекстах…

from os import scandir def filemap(path, action, is_target): with scandir(path) as it: for entry in it: if entry.is_dir(): filemap(entry.path, action, is_target) elif is_target(entry): action(entry)
Code language: JavaScript (javascript)

Параметр ‘action’ используется для передачи callback функции, которая принимает на вход экземпляр объекта os.DirEntry.

Эта функция применит действие (удалить, переместить и т.д…) к файлу.

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

filemap("c:\\Temp", lambda e: print(e.name), lambda e: e.name.lower().endswith(".py"))
Code language: CSS (css)

Мы отображаем имя каждого исходного файла Python, присутствующего в дереве “C:\Temp”.

Подсчет файлов

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

Самая очевидная реализация была бы такой: :

count = 0 def count_file(entry): global count count += 1 filemap("c:\\Temp", count_file, lambda e: e.name.lower().endswith(".py")) print(count)
Code language: PHP (php)

count_file’ действительно считает файлы, но это не соответствует концепции чистоты функционального программирования, поскольку зависит от глобальной переменной.

Если бы я забыл сбросить ‘count’ между двумя вызовами ‘filemap’, я бы получил неправильный результат.

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

Благодаря объекту мы можем хранить состояние счетчика, сохраняя его чистоту…

class CountFile: def __init__(self): self.__count = 0 @property def count(self): return self.__count def __call__(self, entry): self.__count += 1 def __str__(self): return F"{self.__count}" c = CountFile() filemap("c:\\Temp", c, lambda e : e.name.lower().endswith( ".py" )) print(c)

__call__ – это действие, которое вызывается и позволяет объекту вести себя как функция.

Таргетирование

Таргетинг по расширениям

В нашем предыдущем примере мы нацелились на исходные файлы Python с помощью лямбда-функции

lambda e: e.name.lower().endswith(".py")
Code language: CSS (css)

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

Нам нужен более простой и быстрый способ таргетироваться на тип расширения или группу расширений.

Итоговое решение :

def endswith(*args): extensions = [ext.lower() for ext in args] def _is_target(entry): name = entry.name.lower() for ext in extensions: if name.endswith(ext): return True return False return _is_target c = CountFile() filemap("C:\\Tools\\Anaconda3", c, endswith(".py", ".txt")) print(c)
Code language: PHP (php)

Таргетинг по дате создания

При управлении рабочими директориями нам часто приходится настраивать инструменты для удаления файлов в соответствии с датами их создания: мы удаляем файлы, созданные более 30 дней назад.

def older_than(day_count): from datetime import datetime as DT, timedelta, timezone today = DT.now() epoch = DT(1970, 1, 1) ref = timedelta(hours = day_count * 24) def _is_target(entry): creation_date = epoch + timedelta(seconds = entry.stat().st_ctime) return (today - creation_date) >= ref return _is_target filemap("C:\\Temp", lambda e: unlink( e.path ), older_than(30))
Code language: JavaScript (javascript)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *