Все та же проблема: список папок и дисков
В последней статье мы использовали рекурсивную функцию размером менее 10 строк для решения проблемы сканирования папок и ранжирования файлов по дате изменения и размеру.
Теперь я подниму планку и покажу, как можно было сделать лучше.
Объединяем пути с помощью Pathlib
Старые идеи в новом обличье?
Предыдущее решение с соединением путей выглядело следующим образом:
path_file = os.sep.join([path_dir, filename])
Преимущество такого подхода заключается в том, что решение не зависит от операционной системы, и вам не нужно складывать строки с помощью оператора + или форматирования.
Тем не менее, здесь можно допустить ошибку, например, непреднамеренно или ошибочно определить путь к каталогу с помощью закрывающего разделителя.
path_dir: str = r"C:/Users/sselt/Documents/blog_demo/" # abschlieender Trennerfilename: str = "some_file"path_file = os.sep.join([path_dir, filename])# C:/Users/sselt/Documents/blog_demo/\some_file
Несмотря на то, что в этом примере показан рабочий код, неправильный разделитель приведет к ошибке при вызове этого пути. И такие ошибки могут возникать всякий раз, когда далекие от кода пользователи оперируют путями в конфигурационных файлах, не обращая внимания на соглашения.
В Python 3.4 появилось лучшее решение модуль
pathlib
. Он обрабатывает функции файлов и папок модуля
os с помощью объектно-ориентированного подхода.
Напомню, старый вариант выглядел вот так:
import ospath = "C:/Users/sselt/Documents/blog_demo/"os.path.isdir(path)os.path.isfile(path)os.path.getsize(path)
А вот альтернативный:
from pathlib import Pathpath: Path = Path("C:/Users/sselt/Documents/blog_demo/")path.is_dir()path.is_file()path.stat().st_size
Оба варианта дают один и тот же результат. Так чем же второй вариант лучше?
Объектно-ориентированный и более устойчивый к ошибкам
Вызовы в основном являются объектно-ориентированными, нравится
вам это или нет, но лично мне такой подход по душе. Здесь у нас
есть такой объект, как определение path
, у которого
есть атрибуты и методы.
Однако пример с операторами перегрузки в данном случае более интересен:
filename: Path = Path("some_file.txt")path: Path = Path("C:/Users/sselt/Documents/blog_demo")print( path / filename )# C:\Users\sselt\Documents\blog_demo\some_file.txt
Сначала разделение на два пути кажется недопустимым. Однако
объект path
был перегружен так, чтобы работать как
объединенный путь.
В дополнение к этому синтаксическому сахару объекты
path
будут перехватывать другие типичные ошибки:
filename: Path = Path("some_file.txt")# hier path mit berflssigem Trenner am Schlusspath: Path = Path("C:/Users/sselt/Documents/blog_demo/")# hier path mit doppeltem Trennerpath: Path = Path("C:/Users/sselt/Documents/blog_demo//")# hier path vllig durcheinanderpath: Path = Path("C:\\Users/sselt\\Documents/blog_demo") # hier ein wilder Mix# alle Varianten fhren zum selben Ergebnisprint(path/filename)# C:\Users\sselt\Documents\blog_demo\some_file.txt
Такой вариант не только приятнее, но и устойчивее к неправильным
входным данным. В дополнение к другим преимуществам код также не
привязан к определенной операционной системе. Он определяет только
generic объект path
, который объявляется в системе
Windows как WindowsPath
, а в Linux как
PosixPath
.
Большинство функций, которые ожидают строку в качестве пути,
могу работать непосредственно с путем. В редких случаях вам может
понадобиться изменить объект просто с помощью
str(Path)
.
Обработка пути с помощью os.walk
В своей последней статье я использовал
os.listdir
, os.path.isdir
и
рекурсивную функцию для итерации по дереву путей и разграничения
файлов и папок.
Но os.walk
предлагает решение получше. Этот метод
создает не список, а итератор, который можно вызывать построчно. В
результате мы получим соответствующий путь к папке и список всех
файлов по этому пути. Весь процесс происходит рекурсивно, поэтому
вы получите все файлы одним вызовом.
Лучшее решение с os.walk и Pathlib
Если вы объедините два вышеупомянутых метода, то получите решение, которое будет более простым, полностью независимым от операционной системы, устойчивым к неправильным форматам путей и без явных рекурсий:
filesurvey = []for row in os.walk(path): # row beinhaltet jeweils einen Ordnerinhalt for filename in row[2]: # row[2] ist ein tupel aus Dateinamen full_path: Path = Path(row[0]) / Path(filename) # row[0] ist der Ordnerpfad filesurvey.append([path, filename, full_path.stat().st_mtime, full_path.stat().st_size])
Если вам удастся улучшить этот вариант, не постесняйтесь рассказать мне об этом. Я был бы рад вашим отзывам!
Первую часть статьи можно найти здесь.
Перевод статьи подготовлен в преддверии старта курса Python Developer. Basic.
Также приглашаем всех желающих принять участие в бесплатном демо-уроке курса на тему Три кита: map(), filter() и zip().
Можно ли писать код, требующий циклов, но без циклов? Может ли он быть быстрее, чем, если бы мы использовали циклы в Python? Для реализации задуманного понадобится знание слов "callback", "iterator" и "lambda". Если интересно присоединяйтесь!