Прикладна аналітика при розробці IT
КНУ імені Тараса Шевченка, ФІТ
До цього часу більшість програм, які ми писали, просто зберігали всю інформацію в пам’яті, тобто в змінних або всередині самої програми.
Недоліком цього є те, що як тільки програма завершує роботу, все, що ви ввели, все, що ви робили з цією програмою, втрачається.
Використовуючи файли, ви можете зберігати інформацію довгостроково, і введення/виведення файлів (англ. file I/O) в контексті програмування - це написання коду, який може читати з файлів, тобто завантажувати інформацію з них, або записувати до них, тобто зберігати інформацію у самих файлах.
Для початку пропоную розглянути знайому структуру даних, яку ми бачили раніше - list
.
Створимо програму names.py
, яка буде зберігати імена у списку, а потім виводити їх на екран:
Припустімо, що ми хочемо додати підтримку збереження декількох імен, наприклад трьох. Для цього ми можемо використати список. Для цього необхідно створити пустий список names
і додавати (append
) до нього імена, які вводить користувач. Вивід імен відсортуємо за алфавітом:
Як Вас звати? Гаррі
Як Вас звати? Рон
Як Вас звати? Герміона
Привіт, Гаррі!
Привіт, Герміона!
Привіт, Рон!
Звичайно, якщо я запущу цю програму ще раз, всі імена пропадуть. Було б непогано мати можливість якось зберігати цю інформацію. І саме тут з’являється ввід-вивід файлів, і саме тут з’являються файли.
Давайте перепишемо нашу програму так, щоб вона зберігала імена у файлі names.txt
.
Для цього нам необхідно відкрити файл, використовуючи функцію open()
- ця функція приймає два аргументи: ім’я файлу і режим відкриття.
Режим відкриття може бути:
r
(англ. read) - читання, це режим за замовчуванням.w
(англ. write) - запис, цей режим перезаписує файл.a
(англ. append) - дописування, цей режим додає дані до файлу.Якщо файл не існує, то він буде створений. Давайте перепишемо нашу програму з використанням функції open()
:
Запустимо цю програму і перевіримо, чи вона працює:
Відкриємо створений файл:
Все працює! Тепер давайте виконаємо цю програму ще раз, але цього разу введемо ім’я Рон
:
Відкриємо створений файл:
Як бачимо, файл перезаписався, і тепер в ньому знаходиться тільки ім’я Рон
.
Якщо ми хочемо додати ім’я до файлу, а не перезаписати його, то використовуйте режим a
.
Видаліть файл names.txt
і давайте перепишемо нашу програму так, щоб вона дописувала імена до файлу names.txt
:
Запустимо цю програму і перевіримо, чи вона працює:
Запустимо програму ще раз і спробуємо додати ім’я Гаррі
та Рон
:
Зовсім не той результат, який ми очікували.
Імена записалися в один рядок. Це тому, що функція write
не додає символ переносу рядка (\n
) після запису імені.
Щоб це виправити, ми можемо додати символ переносу рядка після запису імені:
Запустимо цю програму:
Terminal
Примітка
Документація до функції open
: https://docs.python.org/3/library/functions.html#open
Під час написання коду дуже легко забути закрити файли і це може стати проблемою. Тому ми можемо піти більш безпечним шляхом і використовувати контекстний менеджер.
Контекстний менеджер - це спеціальна конструкція мови Python, яка дозволяє виконувати певні дії до входу в блок коду і після виходу з блоку коду.
Для використання контекстного менеджера використовується ключове слово with
. Давайте перепишемо нашу програму з використанням контекстного менеджера:
Такий підхід не змінює функціональність програми, але є більш пітоничним.
Для читання, у функції open
використовується режим r
.
Давайте створимо програму names_read.py
, яка буде читати імена з файлу names.txt
і виводити їх на екран:
Для читання використаємо метод readlines
, яка повертає список рядків, які містяться у файлі.
Цей метод повертає список, тому ми можемо використати цикл for
для виведення імен на екран. Також слід врахувати, що метод readlines
повертає список, в якому кожен рядок містить символ переносу рядка (\n
).
Щоб цього уникнути, ми можемо використати метод rstrip
, який видаляє символ переносу рядка з кінця рядка:
Але у попередньому прикладі ми двічі проходимось по всьому файлу: спочатку ми читаємо його у список, а потім виводимо список на екран.
Це не є найкращим рішенням, оскільки ми можемо витратити багато пам’яті, якщо файл дуже великий.
Тому ми можемо використати цикл for
безпосередньо для читання файлу:
Тепер трошки ускладнимо задачу.
Припустимо, що ми хочемо виводити привітання у алфавітному порядку. Для цього нам необхідно відсортувати список імен.
Для цього ми можемо використати функцію sorted()
, яка повертає відсортований список:
Ми можемо зробити цю програму більш компактною. Для цього ми можемо відсортувати сам файл:
Для зворотного сортування ми можемо використати параметр reverse
функції sorted
:
Примітка
Документація до функції sorted
: https://docs.python.org/3/library/functions.html#sorted
Файли csv (англ. comma-separated values, значення, розділені комами) - це файли, які містять дані у вигляді таблиці, де значення розділені комами.
Давайте створимо файл students.csv
:
Запишемо у нього імена і додамо гуртожиток:
Тепер давайте створимо програму students.py
, яка буде читати цей файл.
Ми можемо використати метод split
для розділення рядка на частини. Давайте перепишемо нашу програму з використанням методу split
:
Коли у вас є змінна, яка є списком, наприклад row
, вам не обов’язково переносити всі ці змінні у окремий список.
Ви можете розпакувати всю послідовність одразу.
Іншими словами, якщо ви знаєте, що функція типу split
повертає список, який містить два елементи, ви можете розпакувати цей список у дві змінні:
Уявімо, що нам треба відсортувати цей список даних.
Для цього я можу використати функцію sorted()
і вказати, що я хочу сортувати за другим елементом списку:
З технічної точки зору, це працює, але це не є найкращим рішенням, оскільки дані сортуються по цілому реченню.
Ми можемо вирішити таку задачу за допомогою словників.
Для цього нам необхідно створити пустий словник student_dict
і додавати до нього інформацію про студентів:
students_lst = []
with open('students.csv', 'r', encoding="utf8") as file:
for line in file:
name, house = line.rstrip().split(',')
student_dict = {}
student_dict['name'] = name
student_dict['house'] = house
students_lst.append(student_dict)
for student in students_lst:
print(f'{student["name"]} живе в гуртожитку {student["house"]}')
Ми можемо скоротити код шляхом присвоєння значень словнику одразу:
students_lst = []
with open('students.csv', 'r', encoding="utf8") as file:
for line in file:
name, house = line.rstrip().split(',')
student_dict = {'name': name, 'house': house}
students_lst.append(student_dict)
for student in students_lst:
print(f'{student["name"]} живе в гуртожитку {student["house"]}')
Але результат все ще не відсортований.
Функція sorted
приймає параметр key
, який вказує, за яким ключем сортувати.
Для цього ми можемо використати функцію get_name
, яка повертає ім’я студента і використаємо її як параметр key
:
students_lst = []
with open('students.csv', 'r', encoding="utf8") as file:
for line in file:
name, house = line.rstrip().split(',')
student_dict = {'name': name, 'house': house}
students_lst.append(student_dict)
def get_name(student):
return student['name']
for student in sorted(students_lst, key=get_name):
print(f'{student["name"]} живе в гуртожитку {student["house"]}')
Якщо ж я захочу відсортувати за гуртожитком у зворотному порядку, то я можу використати функцію get_house
і додати параметр reverse=True
у функцію sorted
:
students_lst = []
with open('students.csv', 'r', encoding="utf8") as file:
for line in file:
name, house = line.rstrip().split(',')
student_dict = {'name': name, 'house': house}
students_lst.append(student_dict)
def get_house(student):
return student['house']
for student in sorted(students_lst, key=get_house, reverse=True):
print(f'{student["name"]} живе в гуртожитку {student["house"]}')
Драко живе в гуртожитку Слизерин
Гаррі живе в гуртожитку Гріфіндор
Герміона живе в гуртожитку Гріфіндор
Рон живе в гуртожитку Гріфіндор
Попередження
Зверніть увагу, що в якості аргументу key
функції sorted
ми передаємо функцію get_house
, без дужок. Ми хочемо передати функцію, а не викликати її.
У попередньому прикладі ми використовували функції get_name
і get_house
, які одразу використовуємо і більші ніколи до них не повертаємось.
Ми можемо спростити цей код і використати анонімні функції (англ. lambda functions), які дозволяють нам визначити функцію в одному рядку:
students_lst = []
with open('students.csv', 'r', encoding="utf8") as file:
for line in file:
name, house = line.rstrip().split(',')
student_dict = {'name': name, 'house': house}
students_lst.append(student_dict)
for student in sorted(students_lst, key=lambda student: student['name']):
print(f'{student["name"]} живе в гуртожитку {student["house"]}')
csv
Давайте змінимо файл students.csv
і замінимо гуртожитки на будинки де вони виросли:
Тепер давайте виведемо ці дані на екран:
students_lst = []
with open('students.csv', 'r', encoding="utf8") as file:
for line in file:
name, home = line.rstrip().split(',')
student_dict = {'name': name, 'home': home}
students_lst.append(student_dict)
for student in sorted(students_lst, key=lambda student: student['home']):
print(f'{student["name"]} з {student["home"]}')
У нас виникла помилка. Це тому, що у нас є рядок, який містить дві коми, а ми спробували розпакувати його у дві змінні. Для вирішення цієї проблеми ми можемо використати в якості роздільника якийсь менш популярний символ, наприклад |
:
csv
Інший варіант - це помістити значення у лапки:
В будь-якому випадку необхідно буде змінювати код і продумувати логіку читання файлу. І це стає дуже незручним і складним, якщо у вас є багато різних файлів, які містять дані у різних форматах. Тому для роботи з csv-файлами використовують спеціальний пакет csv
. Давайте перепишемо нашу програму з використанням пакету csv
:
import csv
students_lst = []
with open('students.csv', 'r', encoding="utf8") as file:
reader = csv.reader(file)
for row in reader:
student_dict = {'name': row[0], 'home': row[1]}
students_lst.append(student_dict)
for student in sorted(students_lst, key=lambda student: student['home']):
print(f'{student["name"]} з {student["home"]}')
Якщо ми чітко знаємо кількість стовпчиків у .csv-файлі, ми можемо розпакувати рядки одразу у змінні:
import csv
students_lst = []
with open('students.csv', encoding="utf8") as file:
reader = csv.reader(file)
for name, home in reader:
students_lst.append({'name': name, 'home': home})
for student in sorted(students_lst, key=lambda student: student['home']):
print(f'{student["name"]} з {student["home"]}')
csv
Часто у табличних файлах перший рядок = за назву змінних. Давайте додамо name
та home
у students.csv
:
В таких випадках ми можемо використати функцію DictReader
, яка повертає словник, а не список:
import csv
students_lst = []
with open('students.csv', encoding="utf8") as file:
reader = csv.DictReader(file)
for row in reader:
students_lst.append({'name': row['name'], 'home': row['home']}) # або students_lst.append(row)
for student in sorted(students_lst, key=lambda student: student['home']):
print(f'{student["name"]} з {student["home"]}')
Такий підхід є більш стійким до змін у файлі: хтось міг змінити порядок стовпчиків, але програма все одно буде працювати.
Примітка
Документація до пакету csv
: https://docs.python.org/3/library/csv.html
csv
Припустимо, що ми створюємо програму, яка буде записувати дані про студентів у файл students.csv
. Залишимо у файлі students.csv
наступні дані:
Давайте перепишемо програму students.py
, яка буде записувати дані у файл students.csv
:
Запустимо цю програму:
Відкриємо файл students.csv
:
Як бачите, Python автоматично взяв рядок з комою у лапки щоб уникнути помилки.
csv
Існує ще один спосіб реалізувати програму students.py
не турбуючись про порядок змінних у списку. Для цього ми можемо використати функцію DictWriter
, яка дозволяє записувати дані у файл у вигляді словника:
Запустимо цю програму:
Відкриємо файл students.csv
:
Бінарні файли - це файл, який складається лише з нулів та одиниць і дозволяє зберігати будь-які дані: зображення, відео, звук, текст, тощо.
В Python є популярна бібліотека під назвою pillow
, яка дозволяє працювати з зображеннями, застосовувати фільтри, як в Instagram, створювати анімації, тощо.
Примітка
Документація до пакету PIL
: https://pillow.readthedocs.io
Давайте створимо анімоване GIF-зображення. Сьогодні такі файли зустрічаються скрізь у вигляді мемів, анімацій, наклейок тощо. Анімоване GIF-зображення – це графічний файл, який містить кілька зображень всередині, а комп’ютер показує їх одне за одним, створюючи ефект анімації.
Почнемо з двох статичних зображень:
Примітка
Ці коти походять з мови програмування MIT під назвою Scratch.
Посилання на зображення ви можете знайти у репозиторії: https://github.com/Aranaur/py4ds/tree/main/img/python
Тепер створимо файл costume.py
, який буде об’єднувати ці два зображення у анімацію:
Для цього нам необхідно використати функцію Image.open
, яка дозволяє відкрити зображення, а потім використати метод save
, який дозволяє зберегти зображення у форматі GIF:
Згустимо програму:
Відкриємо файл costume.gif
: