Практические применения

С помощью короткой программы на питоне можно решить довольно широкий спектр задач которые до появления компьютеров просто не существовали. Это работа с файлами и папками операционной системы, работа с изображениями, mp3, pdf, электронной почтой, получение информации из сети интернет и много другое. Почти для всех задач, которые возникают, в сети можно найти подходящий рецепт на языке питон. На данный момент самым большим источником таких рецептов является сайт http://stackoverflow.com. Увы, он на английском, но у него есть и русская версия (в 1000 раз более бедная). Впрочем обычно не нужно много английского, чтобы понять, что происходит.

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

В этом листке мы будем пытаться научиться двум вещам: применять питон в практических задачах, и искать пути решения своих технических задач в интернете.

Если вы нашли рецепт, то следует убедиться, что рецепт на Python3, а не на Python2. Их легко отличить по print без скобок (такая конструкция в Python3 не работает). Зачастую print — это единственное, что нужно поправить. Но это, увы, не всегда так.

В каждой задаче нужно написать программу, которая решает поставленную проблему. В тестовую систему необходимо сдать текст программы, строчку-разделитель ####################, а затем ответ на задачу. Например, если задача — «Вычислить 2 в сотой степени», то возможный правильный ответ такой:

print(2 ** 100)
####################
1267650600228229401496703205376

Задачи 1-5, 26.11.2015

01. Список файлов в папках

В архиве вы найдёте структуру папок с таксономией приматов (виды, семейства и т.д.). Где-то в глубине там даже есть человек разумный :). Необходимо вывести список из всех файлов, которые есть в папке Primates или её подпапках. Порядок файлов не важен.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

Пример ответа:
import os
...
####################
Gorilla_beringei_beringei.txt
Gorilla_beringei_graueri.txt
Gorilla_gorilla_diehli.txt
Gorilla_gorilla_gorilla.txt
Gorilla_gorilla_uellensis.txt
Homo_sapiens.txt
Pan_paniscus.txt
Pan_troglodytes_ellioti.txt
Pan_troglodytes_schweinfurthii.txt
Pan_troglodytes_troglodytes.txt
Pan_troglodytes_vellerosus.txt
Pan_troglodytes_verus.txt
Pongo_abelii.txt
Pongo_pygmaeus_pygmaeus.txt

02. Список файлов в папках с полными путями

В условиях задачи 01 выведите не только имена файлов, но и полные пути до них от начальной папки Primates.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

Пример ответа:
import os
...
####################
Hominidae\Homininae\Gorilla\Gorilla_beringei\Gorilla_beringei_beringei.txt
Hominidae\Homininae\Gorilla\Gorilla_beringei\Gorilla_beringei_graueri.txt
Hominidae\Homininae\Gorilla\Gorilla_gorilla\Gorilla_gorilla_diehli.txt
Hominidae\Homininae\Gorilla\Gorilla_gorilla\Gorilla_gorilla_gorilla.txt
Hominidae\Homininae\Gorilla\Gorilla_gorilla\Gorilla_gorilla_uellensis.txt
Hominidae\Homininae\Homo\Homo_sapiens.txt
Hominidae\Homininae\Pan\Pan_paniscus.txt
Hominidae\Homininae\Pan\Pan_troglodytes_ellioti.txt
Hominidae\Homininae\Pan\Pan_troglodytes_schweinfurthii.txt
Hominidae\Homininae\Pan\Pan_troglodytes_troglodytes.txt
Hominidae\Homininae\Pan\Pan_troglodytes_vellerosus.txt
Hominidae\Homininae\Pan\Pan_troglodytes_verus.txt
Hominidae\Ponginae\Pongo\Pongo_abelii.txt
Hominidae\Ponginae\Pongo\Pongo_pygmaeus\Pongo_pygmaeus_pygmaeus.txt

03. Прочитать все файлы в папке

В каждом файле из задачи 01 записан идентификатор этого таксона в открытом дереве жизни (Open Tree of Life). Выведите список этих идентификаторов.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

Пример ответа:
import os
...
####################
ott624503
ott317556
ott835035
ott417953
ott620805
ott770315
ott158484
ott112831
ott752845
ott767875
ott84217
ott752847
ott770295
ott770308

04. Список всех подпапок

В условиях задачи 01 выведите список имён всех подпапок в папке Primates или её подпапках.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

Пример ответа:
import os
...
####################
Homininae
Ponginae
Gorilla
Homo
Pan
Gorilla_beringei
Gorilla_gorilla
Pongo
Pongo_pygmaeus

05. Список папок с полными путями

В условиях задачи 04 выведите полные пути к папкам от Primates.

Если бы стартовой была папка Primates\Haplorrhini\Simiiformes\Catarrhini\Hominoidea\Hominidae, то ответ был бы такой:

Пример ответа:
import os
...
####################
Hominidae\Homininae
Hominidae\Ponginae
Hominidae\Homininae\Gorilla
Hominidae\Homininae\Homo
Hominidae\Homininae\Pan
Hominidae\Homininae\Gorilla\Gorilla_beringei
Hominidae\Homininae\Gorilla\Gorilla_gorilla
Hominidae\Ponginae\Pongo
Hominidae\Ponginae\Pongo\Pongo_pygmaeus

Решение задач 1-5

В целом задача достаточно простая. Приведём универсальное решение:

import os  # В этом модуле множество функций для работы с операционной системой
path_to_folder = 'C:\\some\\path\\Primates'  # Здесь нужно не забывать экранировать '\'
os.chdir(path_to_folder)  # Не обязательно, но может быть удобно
for (dirpath, dirnames, filenames) in os.walk('.'):  # '.' --- это текущая папка. В предыдущей строчке мы её установили
    # dirpath --- это путь до той директории, которая сейчас обрабатывается
    # dirnames --- список директорий в ней
    # filenames --- список файлов в ней
    for filename in filenames:
        print(filename)  # Просто имя файла
        print(os.path.join(dirpath, filename))  # Имя с путём. os.path.join склеивает две части пути
        f = open(os.path.join(dirpath, filename), 'r', encoding='utf-8')  # Если есть путь к файлу, то его можно открыть
        file_data = f.read()  # И прочитать
        f.close()
        print(file_data)
    for dirname in dirnames:
        print(dirname)  # Просто имя директории
        print(os.path.join(dirpath, dirname))  # Имя с путём

Восстановление фотографий

В новой серии задач мы отрабатываем следующий сценарий: из-за резкого скачка электричества ваш компьютер вырубился во время записи файла на диск. В результате повреждена файловая система, поэтому уже невозможно найти свои файлы в "обычных" местах. На диске остались фотографии, которые вам дороги. С помощью специальной программы, которая сканирует весь диск и ищет последовательности байтов, похожие на фотографии, вы добыли всё, что можно. Так как файловая система умерла, то имён файлов и прочих атрибутов нет, есть только гора файлов с бессмысленными именами. Так как вы много работали с фотографиями, то там много повторов фотографий. А вот и архив с этими фотографиями.

План по наведению порядка:

  • Получить список файлов;
  • Посчитать md5-хэш каждого файла. У одинаковых фотографий будет одинаковый хеш, а у разных — разный;
  • Отобрать по одному файлу для каждого хеша, а остальные — удалить;
  • В каждом файле в EXIF найти информацию о дате и времени съёмки;
  • Переименовать каждый файл в соответствии с датой и временем съёмки.
Для того, чтобы каждый раз не клеить полный путь к имени файла, удобно перейти прямо в директорию к фотографиям при помощи
os.chdir('C:\\some\\path\\resc_photos')
Итак, поехали!

06. md5-хеши файлов

Для каждого файла из архива посчитайте его md5-хеш. Выведите все пары (ИМЯ_ФАЙЛА, ХЕШ). Порядок файлов неважен.

Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

Пример ответа:
import os
import hashlib
...
####################
0spuuzog.jpg 2b04cbfba96acc2b20fb41a65c00116d
1e1ztfom.jpg a46e61e2cf9725d569ad755dbdd189c0
82t56870.jpg 2b04cbfba96acc2b20fb41a65c00116d

07. Отбор уникальных файлов

Отберите все уникальные фотографии и выведите их имена.

Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

Пример ответа:
import os
import hashlib
...
####################
0spuuzog.jpg
1e1ztfom.jpg

08. Удаление лишнего

Получите список всех файлов, которые не лежат в списке из задачи 07. Удалите все эти повторяющиеся файлы.

В тестовую систему необходимо сдать код программы, которая делает это, а также выводит список удаляемых файлов. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

Пример ответа:
import os
import hashlib
...
####################
82t56870.jpg

09. Дата съёмки

Известно, что фотографии сняты в августе 2015 года. В начале каждой фотографии находится EXIF — метаданные, например, дата-время съёмки, модель камеры и т.д. Теоретически их можно расшифровать, но сейчас в этом нет необходимости. Нужную дату можно найти и так. Откройте файл при помощи notepad++, и вы её увидите. После этого будет не сложно добыть дату для каждой фотографии автоматически. Здесь следует учитывать, что данные в файле не текстовые, а бинарные, поэтому открывать файл стоит так:

open(filename, 'rb')
Здесь r отвечает за чтение, а b — за то, что чтение бинарных данных. Бинарные строки очень похожи на обычные, только в коде перед ними стоит буква r. С ними можно делать почти все операции, что и с обычными текстовыми. Те символы бинарной строки, код которых не лежит в диапазоне acsii (от 32 до 127), выводятся как их код в 16-ричной системе счисления. Для получения из обычной текстовой строки бинарной используется метод encode:
>>> print('Привет, world!'.encode('utf-8'))
b'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82, world!'
Для обратного преобразования — метод decode:
>>> print(b'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82, world!'.decode('utf-8'))
Привет, world!

Выведите для каждого из оставшихся файлов дату и время съёмки в том виде, в котором они добываются из файла. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

Пример ответа:
import os
...
####################
0spuuzog.jpg b'2015:08:23 14:26:12'
1e1ztfom.jpg b'2015:08:19 16:05:07'

10. Переименование

Теперь ничего не стоит переименовать файлы в имена вида "yyyy-mm-dd_hh-mm-ss.jpg". Ну, почти ничего, кроме того, что некоторые фотографии сняты в одну и ту же секунду. Если вдруг так окажется, то добавьте в конец имени файла перед ".jpg" один, два или три символа "_" (например, "2015-08-23_14-26-12_.jpg").

В тестовую систему необходимо сдать код программы, которая делает это, а также выводит имена новых файлов. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

Пример ответа:
import os
...
####################
2015-08-23_14-26-12.jpg
2015-08-19_16-05-07.jpg

11. Чтение EXIF

Решение в предыдущих двух задачах не сработает, если у нас будет архив фотографий за 10 лет. Мы не сможем уверенно искать дату съёмки по строке "2015.08", ведь это может оказаться датой изменения, а не съёмки. Поэтому более правильное решение — расшифровать EXIF. Стандарт открыт, поэтому можно написать свою программу по его расшифровке. Однако это достаточно трудоёмко. Один из плюсов питона в том, что большинство подобных задач уже решены, и всё, что требуется, это найти подходящий пакет. В данном случае один из подходящих нам пакетов — это exifread.

Установка пакета exifread
Установка пакетов для Python в операционной системе Windows, увы, зачастую вызывает затруднения. Чтобы его установить, нужно выполнить в командной строке pip install exifread (win+R, затем cmd). Если у вас старая версия Python, не установлен pip, или при установке вы не поставили галочку "Добавить Python в PATH", то это не сработает. Тогда нужно скачать архив с пакетом, распаковать его, зайти внутрь папки exif-py-2.1.2, на пустом месте сделать клик правой кнопкой мыши вместе с нажатой клавишей Shift, выбрать "Открыть окно команд", и выполнить python setup.py install. Если и это не поможет, то нужно найти, где лежит исполняемый файл python.exe, и указать полный путь: "C:\Python34\python.exe" "Y:\exif-py-2.1.2\setup.py" install.

Если и это не помогло, то вы — неудачник установите свежий Python 3.5 (32 битный!), при установке не забудьте поставить галочку "Добавить в Path", и попробуйте всё сначала. Весьма вероятно, что вам заодно придётся обновить WingIDE.

Напишите программу, которая получает дату и время съёмки из EXIF для каждой фотографии при помощи модуля exifread. В тестовую систему необходимо сдать код программы, которая делает это, а также выводит полученные временные отметки. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

Пример ответа:
import os
import exifread
...
####################
2015:08:23 14:26:12
2015:08:19 16:05:07

12. Выбор объектива

При выборе объектива для фотоаппарата возникает вопрос: какие диапазон фокусных расстояний (углов поля зрения, см. картинку в википедии) вам необходим. Что выбирать, длиннофокусный объектив, широкоугольный объектив и т.д.? Довольно надёжный способ — посмотреть, какими фокусными расстояними вы пользуетесь.

Напишите программу, которая получает фокусное расстояние кадра из EXIF для каждой фотографии при помощи модуля exifread. Для каждого используемого фокусного расстояния выведите количество кадров (нужно отсортировать по увеличению фокусного расстояния). Постройте график количества кадров в зависимости от фокусного расстояния (нужно показать на экране). Учтите, что вам нужно эквивалентное фокусное расстояние, так как именно оно участвует в маркировке объективов.

В тестовую систему необходимо сдать код программы, которая делает это, а также выводит количества кадров. Если бы в папке были бы только файлы 0spuuzog.jpg, 1e1ztfom.jpg и 82t56870.jpg, то правильный ответ был бы таким:

Пример ответа:
import os
import exifread
...
####################
24: 1
70: 1

13. Разложить по полочкам

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

Будем тренироваться на «кошках». Внутри архива вы найдёте несколько файлов вида 2013-02-04_04-46-01.txt. Нужно разложить их по папкам вида yyyy/mm/dd, где yyyy, mm и dd — соответствующие части даты, а также переименовать: убрать из имени файла дату.

В тестовую систему необходимо сдать zip-архив, в котором находится папка crt_dirs с нужно структурой, а также py-файл с программой. Если бы архив из условия состоял лишь из файлов 2013-01-16_00-24-33.txt, 2013-01-16_16-40-26.txt и 2015-11-25_16-51-47.txt, то в правильная структура в zip файле ответа выглядела бы так:

Пример ответа:
crt_dirs/2013/01/16/00-24-33.txt
crt_dirs/2013/01/16/16-40-26.txt
crt_dirs/2015/11/25/16-40-26.txt
crt_dirs/my_pgm.py