Python: колекції, частина 1: класифікація, загальні підходи і методи, конвертація

Колекція в Python — програмний об'єкт (змінна-контейнер), що зберігає набір значень одного чи різних типів, що дозволяє звертатися до цих значень, а також застосовувати спеціальні функції та методи, що залежать від типу колекції.

Часта проблема при вивченні колекцій полягає в тому, що розібравши кожен тип досить детально, зазвичай потім не приділяється достатньої уваги роз'ясненню картини в цілому, не проводяться чіткі подібності та відмінності між типами, не показується як одну і тугіше завдання вирішувати для кожної з колекцій в порівнянні.

Ось саме цю проблему я хочу спробувати вирішити в даному циклі статей – розглянути ряд підходів до роботи зі стандартними колекціями в Python у порівнянні між колекціями різних типів, а не окремо, як це зазвичай показується в навчальних матеріалах. Крім того, спробую торкнутися деякі моменти, що викликають труднощі і помилки у початківців.

Для кого: для вивчають Python і вже мають початкове уявлення про колекціях і роботі з ними, які бажають систематизувати і поглибити свої знання, скласти їх у цілісну картину.

Будемо розглядати стандартні вбудовані колекційні типи даних в Python: список (list), кортеж (tuple), рядок (string), безлічі (set, frozenset), словник (dict). Колекції з модуля collections розглядатися не будуть, хоча багато з статті має бути придатним і при роботі з ними.

ЗМІСТ:

  1. Класифікація колекцій;
  2. Загальні підходи до роботи з колекціями;
  3. Деякі загальні методи для частини колекцій;
  4. Конвертування колекцій.
1. Класифікація колекцій
image
image

Пояснення термінології:
Индексированность – кожен елемент колекції має свій порядковий номер — індекс. Це дозволяє звертатися до елемента за його порядковому індексу, проводити слайсинг («нарізку») — брати частину колекції вибираючи виходячи з їх індексу. Детально ці питання будуть розглянуті в подальшому в окремій статті.

Унікальність – кожен елемент колекції може зустрічатися в ній тільки один раз. Це породжує вимогу иммутабельности (незмінності) використовуваних типів даних для кожного елемента, наприклад, таким елементом не може бути список.

Мутабельность (змінність) колекції — дозволяє додавати в колекцію нових членів або видаляти їх. Иммутабельная колекція незмінна після її створення.

Примітка для словника (dict):

  • сам словник мутабелен — можна додавати/видаляти нові пари ключ-значення;
  • значення елементів словника — мутабельны і не унікальні;
  • а ось ключі — иммутабельны і унікальні, тому, наприклад, ми не можемо зробити ключем словника список, але можемо кортеж. З унікальності ключів, так само слід унікальність елементів словника — пар ключ-значення.

2. Загальні підходи до роботи з будь-якою колекцією
Розібравшись в класифікації, розглянемо що можна робити з будь-якої стандартної колекцією незалежно від її типу (у прикладах список і словника, але це працює і для всіх інших даних стандартних типів колекцій):

# Задамо початково список і словник (скопіювати перед прикладами нижче):
my_list = ['a', 'b', 'c', 'd', 'e', 'f']
my_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}

2.1 Друк елементів колекції за допомогою функції print()
print(my_list) # ['a', 'b', 'c', 'd', 'e', 'f']
print(my_dict) # {'a': 1, 'c': 3, 'e': 5, 'f': 6, 'b': 2, 'd': 4}
# Не забуваємо, що порядок елементів у неіндексованих колекціях не зберігається.

2.2 Підрахунок кількості членів колекції за допомогою функції len()
print(len(my_list)) # 6
print(len(my_dict)) # 6 - для словника пари ключ-значення вважаються одним елементом. 
print(len('ab c')) # 4 - для рядка елементом є символ 1

2.3 Перевірка належності елемента даної колекції c допомогою оператора in
x in s — поверне True, якщо елемент входить в колекцію s і False — якщо не входить
Є і варіант перевірки не приналежності:x not in s, де є по суті, просто додається заперечення перед булевими значенням попереднього виразу.

my_list = ['a', 'b', 'c', 'd', 'e', 'f']
print('a' in my_list) # True
print('q' in my_list) # False
print('a' not in my_list) # False
print('q' not in my_list) # True

Для словники можливі варіанти, зрозумілі з коду нижче:

my_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
print('a' in my_dict) # True - без вказівки методу пошук по ключам
print('a' in my_dict.keys()) # True - аналогічно прикладу вище
print('a' in my_dict.values()) # False - так як 'а' — ключ не значення
print(1 in my_dict.values()) # True

Можна перевіряти пари? Можна!

print(('a',1) in my_dict.items()) # True
print(('a',2) in my_dict.items()) # False

Для рядка можна шукати не тільки окремий символ, але і підрядок:

print('ab' in 'abc') # True

2.4 Обхід всіх елементів колекції в цикліfor in
В даному випадку, в циклі послідовно перебиратися елементи колекції, поки не будуть перебрані всі з них.

for elm in my_list:
print(elm)

Зверніть увагу на наступні моменти:

  • Порядок обробки елементів для неіндексованих колекцій буде не той, як при їх створенні
  • проходу в циклі словник є свої особливості:

    for elm in my_dict:
    # При такому обході словника, перебираються тільки ключі
    # рівносильно for elm in my_dict.keys()
    print(elm)
    
    for elm in my_dict.values():
    # При бажанні можна пройти лише за значенням
    print(elm)
    

    Але найчастіше потрібні пари ключ(key) — значення (value).

    for key, value in my_dict.items():
    # Прохід .items() повертає кортеж (ключ, значення), 
    # який присвоюється кортежу змінних key, value
    print(key, value)
    

  • Можлива помилка: Не міняйте кількість елементів колекції в тілі циклу під час ітерації з цієї ж колекції! — Це породжує не завжди очевидні на перший погляд помилки.

    Щоб цього уникнути таких побічних ефектів, можна, наприклад, итерировать копію колекції:

    for elm in list(my_list):
    # Тепер можете видаляти і додавати елементи в початковий список my_list,
    # так як ітерація йде за його копії.
    
2.5 Функції min(), max(), sum()
  • min(), max() — пошук мінімального та максимального елемента відповідно — працюють не тільки для числових, але і для строкових значень.
  • sum() — підсумовування всіх елементів, якщо вони всі числові.
print(min(my_list)) # a
print(sum(my_dict.values())) # 21

3.Загальні методи для частини колекцій
Ряд методів у колекційних типів використовується в більш ніж однієї колекції для вирішення завдань одного типу.
image
Пояснення роботи методів і приклади:
  • .count() — метод підрахунку певних елементів для неуникальных колекцій (рядок, список, кортеж), повертає скільки разів елемент зустрічається в колекції.

    my_list = [1, 2, 2, 2, 2, 3]
    print(my_list.count(2)) # 4 примірники елемента рівного 2
    print(my_list.count(5)) # 0 - тобто такого елемента в колекції немає
    

  • .index() — повертає мінімальний індекс переданого елемента для індексованих колекцій (рядок, список, кортеж)

    my_list = [1, 2, 2, 2, 2, 3]
    print(my_list.index(2)) # перший елемент рівний 2 знаходиться за індексом 1 (індексація з нуля!)
    print(my_list.index(5)) # ValueError: 5 is not in list - відсутній елемент видасть помилку!
    

  • .copy() — метод повертає неглибокі (не рекурсивні) копію колекції (список, словник, обидва типи множини).

    my_set = {1, 2, 3}
    my_set_2 = my_set.copy()
    print(my_set_2 == my_set) # True - колекції рівні - містять однакові значення
    print(my_set_2 is my_set) # False - колекції не ідентичні - це різні об'єкти з різними id
    

  • .clear() — метод мутабельных колекцій (список, словник, множина), що видаляє з колекції всі елементи і перетворює її в порожню колекцію.

    my_set = {1, 2, 3}
    print(my_set) # {1, 2, 3}
    my_set.clear()
    print(my_set) # set()
    
Особливі методи порівняння множин (set, frozenset)
  • set_a.isdisjoint(set_b) — істина, якщо set_a і set_b не мають спільних елементів.
  • set_a.issubset(set_b) — якщо всі елементи set_b належать set_a то перше безліч цілком входить у друге і є його підмножиною (set_b — підмножина)
  • set_a.issuperset(set_b) — відповідно, якщо умова вище справедливо, то set_a — надмножество
set_a = {1, 2, 3}
set_b = {2, 1} # порядок елементів не важливий!
set_c = {4}
set_d = {1, 2, 3}

print(set_a.isdisjoint(set_c)) # True - немає спільних елементів
print(set_b.issubset(set_a)) # True - set_b цілком входить у set_a, значить set_b - підмножина
print(set_a.issuperset(set_b)) # True - set_b цілком входить у set_a, значить set_a - надмножество

# При рівності множин вони одновременоо і підмножину і розширеним набором один для одного
print(set_a.issuperset(set_d)) # True
print(set_b.issubset(set_d)) # True

4.Конвертація одного типу колекції в іншій
Залежно від поставлених завдань, один тип колекції можна конвертувати в інший тип колекції. Для цього, як правило достатньо передати одну колекцію функцію створення іншої (вони є в таблиці вище).

my_tuple = ('a', 'b', 'a')
my_list = list(my_tuple)
my_set = set(my_tuple) # втрачаємо індекси та дублікати елементів!
my_frozenset = frozenset(my_tuple) # втрачаємо індекси та дублікати елементів!
print(my_list, my_set, my_frozenset) # ['a', 'b', 'a'] {'a', 'b'} frozenset({'a', 'b'})

Зверніть увагу, що при перетворенні однієї колекції до іншої можлива втрата даних:
  • При перетворенні в безліч губляться дублюючі елементи, так як безліч містить унікальні елементи! Власне, перевірка на унікальність, зазвичай і є причиною використовувати безліч в завданнях, де у нас є в цьому потреба.
  • При конвертації індексованою колекції в неиндексированную втрачається інформація про порядок елементів, а в деяких випадках вона може бути критично важливою!
  • Після конвертації в иммутабельный тип, ми більше не зможемо змінювати елементи колекції — видаляти, редагувати, додавати нові. Це може привести до помилок в наших функції обробки даних, якщо вони були написані для роботи з мутабельными колекціями.
Додаткові деталі:
  • вище Способом не вийде створити словник, так як він складається з пар ключ-значення.

    Це обмеження можна обійти, створивши словник комбінуючи ключі зі значеннями з використанням zip():

    my_keys = ('a', 'b', 'c')
    my_values = [1, 2] # Якщо кількість елементів різне - 
    # буде відпрацьовано поки вистачає на пари - зайві відкинуті
    my_dict = dict(zip(my_keys, my_values))
    print(my_dict) # {'a': 1, 'b': 2}
    

  • Створюємо рядок з іншої колекції:

    my_tuple = ('a', 'b', 'c')
    my_str = ".join(my_tuple)
    print(my_str) # abc
    

  • Можлива помилка: Якщо Ваша колекція містить мутабельні елементи (наприклад список списків), то її можна конвертувати в иммутабельную колекцію, так як її елементи можуть бути тільки иммутабельными!

    my_list = [1, [2, 3], 4]
    my_set = set(my_list) # TypeError: unhashable type: 'list'
Найбільш потужні та гнучкі способи — генератори колекцій будуть розглянуті в окремій статті, так як там багато нюансів і варіантів використання, на яких рідко загострюють увагу і потрібен детальний розбір.

У наступних статтях планується продовження:

  • Колекції: індексація, слайсинг, сортування;
  • Конкатенація колекцій;
  • Тонкощі генерації колекцій.

Запрошую до обговорення:

  • Якщо я десь допустив неточність або не врахував щось важливе — пишіть в коментарях, важливі коментарі будуть пізніше додано статтю з зазначенням вашого авторства.
  • Якщо якісь моменти не зрозумілі і потрібне уточнення — пишіть ваші запитання в коментарях — або я (якщо буде можливість коментувати, оскільки стаття з пісочниці) або інші читачі дадуть відповідь, а слушні питання будуть додані в статтю.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.