Л12. Колекції Python і текст
Коротко про лекцію
Навчимося обирати list, tuple, set і dict за властивостями задачі. Додамо прямий обхід, нормалізацію тексту та обережне використання спискових включень.
Окреме значення рідко достатнє для реальної задачі. Частіше програма працює з оцінками, словами, ролями або записами. Тому треба вибрати структуру, яка відповідає потрібним операціям.
Python має кілька базових колекцій. Їх не варто вчити як список синтаксичних форм. Корисніше питати про порядок, дублікати, змінюваність і спосіб пошуку.
Практична мета. Після лекції ви повинні пояснити вибір колекції та простежити, як дані змінюються під час обробки.
1. list зберігає впорядковану змінювану послідовність
Список, або list, — це впорядкована колекція елементів. Порядок елементів має значення, а список можна змінювати після створення.
Такий об’єкт називають змінюваним. Список записують у квадратних дужках:
scores = [72, 81, 65]
Функція len(scores) повертає кількість елементів. Індекси починаються з нуля, як у знайомих послідовностях C++.
Окремий елемент списку читають квадратними дужками, наприклад scores[0]. Тут 0 — індекс першого елемента.
Метод append(value) додає один елемент у кінець списку. Метод викликається через крапку після об’єкта.
Присвоєння списку іншому імені не створює автоматичну копію. Обидва імена можуть посилатися на той самий змінюваний об’єкт.
Термін: кілька імен одного об’єкта
Кілька імен одного об’єкта означають, що кілька імен посилаються на один змінюваний об’єкт. Зміна через одне ім’я тоді видима через інше.
scores = [72, 81, 65]
alias = scores
alias.append(90)
print(scores)
Зміна через alias видима через scores. Причина — спільний об’єкт списку. Тут кілька імен посилаються на один об’єкт.
flowchart TD
A["scores"] --> L["один список: 72, 81, 65"]
B["alias"] --> L
L --> C["append(90)"]
C --> D["обидва імена бачать 90"]
Перед зміною списку завжди запитайте: хто ще може посилатися на цей об’єкт?
Іноді потрібен незалежний список. Метод copy() створює окремий список із поточними елементами. Подальше append() у копію не змінює початковий список.
scores = [72, 81, 65]
copy_scores = scores.copy()
copy_scores.append(90)
print(scores)
print(copy_scores)
Спільне ім’я чи копія
alias = scores створює ще одне ім’я того самого списку. copy_scores = scores.copy() створює окремий список із такими самими поточними елементами.
2. tuple фіксує послідовність після створення
Кортеж, або tuple, теж зберігає впорядковані елементи. Після створення його елементи не замінюють через індекс.
Такий об’єкт називають незмінним. Кортеж часто записують у круглих дужках.
point = (4, 7)
Коротку відому структуру можна розпакувати в окремі імена.
x, y = point
point = (4, 7)
x, y = point
print(x + y)
Кортеж зручний, коли набір значень має залишатися тією самою групою. Вибір між tuple і list залежить від потрібної змінюваності.
3. set відповідає на питання про належність
Множина, або set, зберігає унікальні елементи. Вона особливо корисна для перевірки належності, коли порядок не впливає на задачу.
Наприклад:
roles = {"student", "teacher", "admin"}
Порожню множину створюють викликом set(). Запис {} створює порожній словник, тому для порожнього set потрібна явна назва типу.
Оператор in перевіряє належність значення колекції:
role in roles
allowed = {"студент", "викладач", "адміністратор"}
role = "студент"
print(role in allowed)
Якщо задача звучить «чи входить значення до дозволеного набору?», set часто виражає її прямо. Повторне додавання того самого елемента не створює дубліката.
4. dict зв’язує ключі зі значеннями
Словник, або dict, зберігає пари ключ: значення. Ключ дає змістовний спосіб знайти пов’язане значення.
Малий приклад:
prices = {"tea": 45, "coffee": 60}
Тут назва товару є ключем, а ціна — значенням. Для лічильника часто треба отримати попереднє число або нуль.
Запис prices["tea"] читає значення за ключем "tea". У словнику квадратні дужки містять ключ, а не позицію.
Метод get(key, default) повертає значення за ключем. Якщо ключа немає, він повертає default.
words = ["кіт", "ліс", "кіт", "ріка", "кіт"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts["кіт"])
Для першого слова get повертає 0. Потім лічильник стає 1. Наступні входження читають уже накопичене значення.
flowchart TD
A["чергове word"] --> B["counts.get(word, 0)"]
B --> C["додати 1"]
C --> D["записати counts[word]"]
D --> A
Цей алгоритм має чіткий стан: словник містить частоти вже обробленої частини послідовності.
5. Обирайте колекцію за потрібною властивістю
Починайте не з назви типу. Починайте з операцій задачі.
| Потреба | Природний вибір |
|---|---|
| впорядкована змінювана послідовність | list |
| коротка фіксована група значень | tuple |
| унікальні значення та перевірка належності | set |
відповідність ключ → значення |
dict |
flowchart TD
A{"потрібні пари ключ-значення?"} -- так --> D["словник"]
A -- ні --> B{"потрібні унікальні значення?"}
B -- так --> S["множина"]
B -- ні --> C{"колекція змінюватиметься?"}
C -- так --> L["список"]
C -- ні --> T["кортеж або інша незмінна форма"]
Це не абсолютна таблиця. Вона допомагає сформулювати першу обґрунтовану гіпотезу.
6. Прямий обхід часто кращий за ручний індекс
Якщо потрібні самі елементи, Python дозволяє обходити колекцію прямо:
for score in scores:
...
Індекс не треба створювати без потреби. Проте іноді потрібні і позиція, і значення.
Для цього Python має функцію enumerate. Вона під час обходу дає пару «номер, елемент».
Функція zip(a, b) об’єднує елементи двох послідовностей попарно. Це корисно, коли ім’я та оцінка зберігаються в паралельних колекціях.
names = ["Оля", "Тарас", "Іра"]
scores = [88, 73, 91]
for position, (name, score) in enumerate(zip(names, scores), 1):
print(position, name, score)
zip формує пари name, score. Другий аргумент 1 задає перший номер для enumerate. Іменовані аргументи Python розберемо пізніше, тому зараз використовуємо позиційний запис.
Важлива передумова: паралельні колекції мають логічно відповідати одна одній. Якщо вони описують один запис, пізніше часто краще створити одну структуру запису.
7. Текст треба нормалізувати за явною політикою
Текст із зовнішніх даних часто потребує нормалізації. Це означає привести різні допустимі форми до узгодженого вигляду.
Термін: нормалізація
Нормалізація застосовує наперед визначені правила до різних допустимих форм даних. Конкретні правила залежать від задачі.
Метод strip() прибирає пробільні символи з обох країв. Метод lower() повертає рядок у нижньому регістрі.
Рядки Python є незмінними об’єктами. Вираз text[0] читає один символ, а присвоєння text[0] = "A" є помилковим. Методи strip() і lower() повертають нові рядки.
Межа змінюваності
list можна змінювати після створення, а str і tuple є незмінними. Тому операція над рядком повертає новий рядок замість зміни окремого символу.
raw = " Python, PYTHON, python "
normalized = raw.strip().lower()
print(normalized)
Ця політика вирішує лише дві конкретні відмінності: крайові пробіли та регістр. Вона не прибирає кому і не виконує повну мовну нормалізацію.
flowchart TD
A["початковий текст"] --> B["strip: прибрати крайові пробіли"]
B --> C["lower: узгодити регістр"]
C --> D["нормалізований текст"]
D --> E["подальший пошук або підрахунок"]
Тому спочатку визначте правило даних. Потім вибирайте методи, які реалізують це правило.
8. Спискове включення стисло виражає просте перетворення
Звичайний цикл добре показує стан і підходить для першого пояснення. Після цього деякі прості перетворення можна записати компактніше.
Спискове включення створює новий список за компактною схемою.
Термін: спискове включення
Спискове включення — компактний запис побудови нового списку під час обходу іншої колекції. Складну логіку зазвичай легше читати у звичайному циклі.
[вираз для елемента for елемент in джерело if умова]
Фільтр if є необов’язковим. Читайте такий запис з частини for: «для кожного елемента джерела…».
temperatures = [12, 18, 21, 9, 24]
warm = [value for value in temperatures if value >= 18]
print(warm)
Алгоритмічно це означає: обійти температури, залишити значення від 18 і додати їх до нового списку. Якщо спискове включення важко прочитати, звичайний цикл буде кращим.
9. Змінюваність впливає на міркування про стан
Для list, set і dict важливо розрізняти дві дії. Перша змінює сам об’єкт. Друга переприв’язує ім’я до нового об’єкта.
Наприклад, append змінює список. Присвоєння нового списку імені змінює зв’язок імені. Для кількох імен одного об’єкта ця різниця визначає, хто побачить результат.
Трасуючи програму, малюйте імена окремо від змінюваних об’єктів. Така модель зменшує кількість «дивних» ефектів у коді.
10. Контрольний алгоритм вибору колекції
Перед написанням коду дайте відповіді на п’ять питань:
- чи важливий порядок;
- чи дозволені дублікати;
- чи потрібна зміна після створення;
- чи потрібен пошук за ключем;
- чи треба зберігати лише унікальні значення.
Після вибору структури визначте спосіб обходу. Потім окремо сформулюйте політику нормалізації зовнішнього тексту. Лише після цього скорочуйте простий цикл списковим включенням.
11. Інтерактивна самоперевірка лекції
Самоперевірка перевіряє роботу кількох імен одного об’єкта, вибір колекції, підрахунок частот, enumerate, zip і текстову нормалізацію. Перед відповіддю трасуйте короткі фрагменти як звичайну програму.