Структури даних#

uk-flagанглійська: Data Structures

Структури даних — це, по суті, і є структури, які можуть зберігати деякі дані разом. Іншими словами, вони використовуються для зберігання даних.

У Python є чотири вбудовані структури даних: список (англ.“list”), кортеж (англ.“tuple”), словник (англ.“dictionary”) та множина (англ.“set”). Ми побачимо, як використовувати кожен із них і як вони полегшують нам життя.

Список#

uk-flagанглійська: List

Список — це структура даних, яка містить упорядкований набір елементів, тобто зберігає послідовність елементів у списку (англ.“a sequence of items in a list”). Це легко уявити, якщо згадати список покупок, в якому перераховується, що потрібно купити, з тим винятком, що у списку покупок кожен елемент зазвичай розміщується в окремому рядку, тоді як у Python вони розділяються комами.

Список елементів має бути укладений у квадратні дужки, щоб Python зрозумів, що ви вказуєте список. Створивши список, ви можете додавати, видаляти або шукати елементи у списку. Оскільки ми можемо додавати та видаляти елементи, ми говоримо, що список є змінним (англ.“mutable”) типом даних, тобто цей тип можна змінювати.

Короткий вступ до об’єктів і класів#

uk-flagанглійська: Quick Introduction To Objects And Classes

Хоч я і намагався досі відтягнути обговорення об’єктів і класів, на даному етапі все ж таки необхідне деяке пояснення, щоб ви краще зрозуміли ідею списків. Ми детально розглянемо цю тему у пізнішому розділі.

Список є прикладом використання об’єктів і класів. Коли ми присвоюємо деякій змінній i значення, скажімо, ціле число 5, ви можете сприймати це як створення об’єкта (тобто екземпляра) i класу (тобто типу) int. Насправді, ви можете прочитати help(int), щоб зрозуміти це краще.

Клас також може мати методи, тобто функції, визначені для використання лише стосовно цього класу. Ви можете використовувати ці частини функціональності, лише якщо у вас є об’єкт цього класу. Наприклад, Python надає метод append для класу список, який дозволяє вам додавати елемент у кінець списку. Наприклад, мій_список.append('якийсь елемент') додасть цей рядок до списку мій_список. Зверніть увагу на використання нотації з крапками для доступу до методів об’єктів.

Клас також може мати поля, які є нічим іншим, як змінними, визначеними для використання лише стосовно цього класу. Ви можете використовувати ці змінні/імена, лише якщо у вас є об’єкт цього класу. Поля також доступні за допомогою нотації з крапками, наприклад, мій_список.field.

код python ds_using_list_ukr.py

# Це мій список покупок 
список_покупок = ['яблуко', 'манго', 'морква', 'банан']

print('Я маю', len(список_покупок), 'товари для покупок.')

print('Товари для покупок:', end=' ')
for елементи in список_покупок:
    print(елементи, end=' ')

print('\nЯ також маю купити рис.')
список_покупок.append('рис')
print('Мій список покупок зараз', список_покупок)

print('Я відсортую свій список зараз')
список_покупок.sort()
print('Сортований список покупок є', список_покупок)

print('Перший товар, який я куплю, це', список_покупок[0])
старий_елемент = список_покупок[0]
del список_покупок[0]
print('Я купив', старий_елемент )
print('Мій список покупок зараз', список_покупок)

Висновок:

Я маю 4 товари для покупок.
Товари для покупок: яблуко манго морква банан 
Я також маю купити рис.
Мій список покупок зараз ['яблуко', 'манго', 'морква', 'банан', 'рис']
Я відсортую свій список зараз
Сортований список покупок є ['банан', 'манго', 'морква', 'рис', 'яблуко']
Перший товар, який я куплю, це банан
Я купив банан
Мій список покупок зараз ['манго', 'морква', 'рис', 'яблуко']

Як це працює

Змінна список_покупок - це список покупок для того, хто збирається на ринок. У список_покупок ми зберігаємо лише рядки назв предметів для покупки, але ви можете додавати будь-які об’єкти до списку, включаючи номери та навіть інші списки.

Ми також використали цикл for…in для ітерації по елементах списку. Наразі ви вже зрозуміли, що список також є послідовністю. Особливості послідовностей буде обговорено у пізнішому розділі.

Зверніть увагу на використання параметра end у виклику функції print, який вказує, що ми хочемо закінчити рядок у висновку пробілом, замість звичайного розриву рядка.

Далі ми додаємо елемент до списку за допомогою методу append- методу об’єкта списку, як вже обговорювалося раніше. Потім ми перевіряємо, чи елемент справді додано до списку, друкуючи вміст списку за допомогою передачі цього списку функціїprint, яка старанно друкує його.

Потім ми сортуємо список за допомогою методу sortоб’єкта списку. Важливо розуміти, що цей метод впливає на сам список і не повертає змінений список - це відрізняється від того, як працюють рядки. Це те, що ми маємо на увазі, кажучи, що списки змінні ( англ.“mutable”), а рядки незмінні ( англ.“immutable”).

Потім, коли ми закінчимо купувати товар на ринку, ми хочемо видалити його зі списку. Ми досягаємо цього за допомогою оператора del. Тут ми згадуємо, який елемент зі списку ми хочемо видалити, і оператор del видаляє його зі списку за нас. Ми вказуємо, що ми хочемо видалити перший елемент зі списку, і тому ми використовуємо del список_покупок[0] (пам’ятайте, що Python починає відлік з 0).

Якщо ви хочете дізнатися про всі методи, визначені об’єктом списку, подробиці дивіться у help(list).

Кортеж#

uk-flagанглійська: Tuple

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

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

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

код python ds_using_tuple_ukr.py

# Я рекомендую завжди використовувати круглі дужки
# позначати початок та кінец кортежу
# незважаючи на те, що дужки необов’язкові.
# Явне краще за неявне.
зоопарк  = ('пітон', 'слон', 'пінгвін')
print('Кількість тварин у зоопарку становить', len(зоопарк))

новий_зоопарк = 'мавпа', 'верблюд', зоопарк # дужки не потрібні, але це гарна ідея
print('Кількість клітин у новому зоопарку становить', len(новий_зоопарк))
print('Усі тварини в новому зоопарку є', новий_зоопарк)
print('Тварини, привезені зі старого зоопарку є', новий_зоопарк[2])
print('Остання тварина, привезена зі старого зоопарку', новий_зоопаркo[2][2])
print('Кількість тварин у новому зоопарку становить',
      len(новий_зоопарк)-1+len(новий_зоопарк[2]))

Висновок:

$ python ds_using_tuple_ukr.py
Кількість тварин у зоопарку становить 3
Кількість клітин у новому зоопарку становить 3
Усі тварини в новому зоопарку є ('мавпа', 'верблюд', ('пітон', 'слон', 'пінгвін'))
Тварини, привезені зі старого зоопарку є ('пітон', 'слон', 'пінгвін')
Остання тварина, привезена зі старого зоопарку пінгвін
Кількість тварин у новому зоопарку становить 5

Як це працює

Змінна зоопарк означає кортеж елементів. Ми бачимо, що функцію len можна використовувати для отримання довжини кортежу. Це також вказує на те, що кортеж також є послідовністю.

Зараз ми переводимо цих тварин у новий зоопарк, оскільки старий зоопарк закривається. Таким чином, кортеж новий_зоопарк містить деяких тварин, які вже там разом із тваринами, привезеними зі старого зоопарку. Повертаючись до реальності, зауважте, що кортеж у кортежі не втрачає своєї ідентичності.

Ми можемо отримати доступ до елементів у кортежі, вказавши позицію елемента в парі квадратних дужок, як ми робили для списків. Це називається оператором індексування (англ.“indexing”). Ми отримуємо доступ до третього елемента в новий_зоопарк, вказуючи новий_зоопарк[2], і отримуємо доступ до третього елемента в третьому елементі в кортежі новий_зоопарк, вказуючи новий_зоопарк[2][2]. Це досить просто, якщо ви зрозуміли ідіому.

Кортеж із 0 або 1 елементом

Порожній кортеж створюється пустою парою круглих дужок, наприклад myempty = (). Однак кортеж з одним елементом не такий простий. Його потрібно вказати за допомогою коми після першого (і єдиного) елемента, щоб Python міг розрізнити кортеж і пару круглих дужок, які оточують об’єкт у виразі. Таким чином, щоб отримати кортеж, що містить елемент 2, вам потрібно буде вказати singleton = (2 , ).

Примітка для програмістів Perl

Список у списку не втрачає своєї ідентичності, тобто списки не розгортаються, як у Perl. Те саме стосується кортежу в кортежі, або кортежу в списку, або списку в кортежі тощо. Що стосується Python, це просто об’єкти, які зберігаються за допомогою іншого об’єкта, от і все.

Словник#

uk-flagанглійська: Dictionary

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

Зауважте, що ви можете використовувати лише незмінні об’єкти (наприклад, рядки) для ключів словника, але ви можете використовувати як незмінні, так і змінні об’єкти для значень словника. По суті, це означає, що ви повинні використовувати лише прості об’єкти для ключів.

Пари ключ-значення вказуються в словнику за допомогою наступної нотації: d = {key1 : value1, key2 : value2 }. Зверніть увагу, що ключ і значення відокремлені двокрапкою, а самі пари відокремлені комами, і все це взято у пару фігурних дужок.

Пам’ятайте, що пари ключ-значення в словнику не впорядковані жодним чином. Якщо вам потрібен певний порядок, вам доведеться самостійно відсортувати словник, перш ніж використовувати його.

Словники, які ви використовуватимете, є екземплярами/об’єктами класу dict.

код python ds_using_dict_ukr.py

# "ab" є скороченням від 'a'ddress'b'ook(address book-адресна книга).

ab = {
    'Swaroop': 'swaroop@swaroopch.com',
    'Larry': 'larry@wall.org',
    'Matsumoto': 'matz@ruby-lang.org',
    'Spammer': 'spammer@hotmail.com'
}

print("Адрес Swaroop'а:", ab['Swaroop'])

# Видалення пари ключ-значення
del ab['Spammer']

print('\nВ адресній книзі {} контакта\n'.format(len(ab)))

for ім_я, адреса in ab.items():
    print('Контакт {} за адресою: {}'.format(ім_я, адреса))

# Додавання пари ключ-значення
ab['Guido'] = 'guido@python.org'

if 'Guido' in ab:
    print("\nАдрес Guido:", ab['Guido'])

Висновок:

$ python3 using_dict_ukr.py
Адрес Swaroop'а: swaroop@swaroopch.com

В адресній книзі 3 контакта

Контакт Swaroop за адресою: swaroop@swaroopch.com
Контакт Larry за адресою: larry@wall.org
Контакт Matsumoto за адресою: matz@ruby-lang.org

Адрес Guido: guido@python.org

Як це працює

Ми створюємо словник ab, використовуючи вже розглянуту нотацію. Потім ми отримуємо доступ до пар ключ-значення, вказуючи ключ за допомогою оператора індексування, як обговорювалося в контексті списків і кортежів. Зверніть увагу на простий синтаксис.

Ми можемо видалити пари ключ-значення за допомогою нашого старого друга - оператора del. Ми просто вказуємо ім’я словника та оператор індексування для ключа, який потрібно видалити, і передаємо його оператору del. Для цієї операції немає необхідності знати значення, яке відповідає ключу.

Далі ми звертаємося до всіх пар ключ-значення нашого словника ,використовуючи метод items, який повертає список кортежів, де кожен кортеж містить пару елементів — ключ та значення. Ми отримуємо цю пару та присвоюємо їй значення змінних ім_я та адреса відповідно до кожної з пар за допомогою циклу for...in, а потім друкуємо ці значення в блоці for.

Ми можемо додати нові пари ключ-значення, просто використовуючи оператор індексування для доступу до ключа та присвоення йому деякого значення, як ми зробили для Guido у наведеному вище випадку.

Ми можемо перевірити, чи існує пара ключ-значення, використовуючи оператор in.

Щоб переглянути список методів класу dict, перегляньте help(dict).

Ключові аргументи і словники

uk-flagанглійська: Keyword Arguments and Dictionaries

Якщо ви використовували ключові аргументи у своїх функціях, ви вже використовували словники! Просто подумайте про це: ви вказали пару ключ-значення серед параметрів функції при її визначенні, а коли звертаєтесь до змінних усередині функції, то це, власне, звернення за ключом до словника (який у термінах розробників компіляторів називається таблицею імен (англ.» symbol table»)).

Послідовність#

uk-flagанглійська: Sequence

Списки, кортежі та рядки є прикладами послідовностей, але що таке послідовності і що в них такого особливого?

Основними функціями є перевірка приналежності (тобто вирази in та not in) і операції індексування, які дозволяють нам напряму отримати певний елемент послідовності.

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

код python ds_seq_ukr.py

список_покупок = ['яблуко', 'манго', 'морква', 'банан']
ім_я = 'swaroop'

# Операція індексування
print('Елемент 0 є', список_покупок[0])
print('Елемент 1 є', список_покупок[1])
print('Елемент 2 є', список_покупок[2])
print('Елемент 3 є', список_покупок[3])
print('Елемент -1 є', список_покупок[-1])
print('Елемент -2 є', список_покупок[-2])
print('Символ 0 є', ім_я[0])

# Зріз зі списку 
print('Елемент з 1 до 3 є', список_покупок[1:3])
print('Елемент з 2 до кінця є', список_покупок[2:])
print('Елемент з 1 до -1 є', список_покупок[1:-1])
print('Елемент від початку до кінця є',список_покупок[:])

# Зріз з рядка 
print('Символи з 1 до 3 є', ім_я[1:3])
print('Символи з 2 до кінця є', ім_я[2:])
print('Символи з 1 до  -1 є', ім_я[1:-1])
print('Символи від початку до кінця є', ім_я[:])

Висновок:

$ python3 ds_seq_ukr.py
Елемент 0 є яблуко
Елемент 1 є манго
Елемент 2 є морква
Елемент 3 є банан
Елемент -1 є банан
Елемент -2 є морква
Символ 0 є s
Елемент з 1 до 3 є ['манго', 'морква']
Елемент з 2 до кінця є ['морква', 'банан']
Елемент з 1 до -1 є ['манго', 'морква']
Елемент від початку до кінця є ['яблуко', 'манго', 'морква', 'банан']
Символи з 1 по 3 є wa
Символи з 2 до кінця є aroop
Символи з 1 до  -1 є waroo
Символи від початку до кінця є swaroop

Як це працює

Спочатку ми бачимо, як використовувати індекси для отримання окремих елементів послідовності. Це також називається операцією приписування індексу (англ.“subscription operation”).

Коли ми вказуємо число у квадратних дужках після послідовності, як показано вище, Python витягує елемент, який відповідає зазначеній позиції в послідовності. Пам’ятайте, що Python починає рахувати числа з 0. Отже, список_покупокt[0] витягує перший елемент, а список_покупокt[3] витягує четвертий елемент у послідовності список_покупок.

Індекс також може бути від’ємним числом. У цьому випадку позиція обчислюється з кінця послідовності. Таким чином, список_покупок[-1] посилається на останній елемент у послідовності, а список_покупок[-2] витягує передостанній елемент у послідовності.

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

Перше число (перед двокрапкою) в операції зрізу вказує позицію, з якої починається зріз, а друге число (після двокрапки) вказує, де зріз має закінчитися. Якщо перше число не вказано, Python розпочне з початку послідовності. Якщо друге число пропущено, Python зупиниться в кінці послідовності. Зверніть увагу,що отриманий зріз буде починатися із зазначеної початкової позиції (англ.“starts”),а закінчуватися перед зазначеної кінцевої позицією (англ.“end”), тобто, початкова позиція буде включена у зріз, а кінцева – ні.

Таким чином, список_покупок[1:3] повертає зріз із послідовності, починаючи з позиції 1, включає позицію 2, але зупиняється на позиції 3, і тому повертає зріз з двох елементів. Так само список_покупок[:] повертає копію всієї послідовності.

Також можна робити зріз використовуючи від’ємні числа. Числа зі знаком мінус використовуються для позицій з кінця послідовності. Наприклад, список_покупок[:-1] поверне зріз послідовності, який виключає останній елемент послідовності, але містить усе інше.

Ви також можете надати третій аргумент для зрізу, який є кроком для зрізу (за замовчуванням розмір кроку дорівнює 1):

>>> список_покупок= ['яблуко', 'манго', 'морква', 'банан']
>>> список_покупок[::1]
['яблуко', 'манго', 'морква', 'банан']
>>> список_покупок[::2]
['яблуко', 'морква']
>>> список_покупок[::3]
['яблуко', 'банан']
>>> список_покупок[::-1]
['банан', 'морква', 'манго', 'яблуко']

Зауважте, що коли крок дорівнює 2, ми отримуємо елементи з позиціями 0, 2,… Коли розмір кроку дорівнює 3, ми отримуємо елементи з позиціями 0, 3,… тощо.

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

Множина#

uk-flagанглійська: Set

Множини - це невпорядковані (англ.»unordered «) набори простих об’єктів. Вони необхідні тоді, коли присутність об’єкта в наборі важливіша за порядок або те, скільки разів даний об’єкт там зустрічається.

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

Скандинавія = set(["Данія","Норвегія","Швеція"])
print("3 країни Скандинавії:", Скандинавія)
print("Чи є Ісландія частиною Скандинавії?: ", "Ісландія" in Скандинавія)
print("Нордичні країни також включають Ісландію та Фінляндію")
Нордичні_країни = Скандинавія.copy() 
Нордичні_країни.add("Ісландія")
Нордичні_країни.add("Фінляндія")
print("Нордичні країни", Нордичні_країни )
print("Чи Нордичні країни є супермножиною Скандинавії?", Нордичні_країни.issuperset(Скандинавія))
print("Чи є Скандинавія підмножиною нордичних країн?", Скандинавія.issubset(Нордичні_країни))
країни_сухопутного_кордону = Нордичні_країни.copy()
країни_сухопутного_кордону.remove("Ісландія")
print("Нордичні країни з сухопутними кордонами: ", країни_сухопутного_кордону)
print("Перетин Скандинавії та Нордичних країн: ", Скандинавія.intersection(Нордичні_країни))

Як це працює

Якщо ви пам’ятаєте базову математику теорії множин зі школи, то цей приклад досить зрозумілий. Але якщо ні, ви можете пошукати в Google «теорію множин» і «діаграму Венна», щоб краще зрозуміти, як ми використовуємо множини в Python.

Посилання#

uk-flagанглійська: References

Коли ви створюєте об’єкт і присвоюєте його змінній, змінна лише посилається на об’єкт і не є цим об’єктом! Тобто ім’я змінної вказує на ту частину пам’яті комп’ютера, де зберігається об’єкт. Це називається прив’язкою (англ.“binding”) імені до об’єкта.

Загалом, вам не потрібно турбуватися про це, проте є деякий неочевидний ефект, про який потрібно пам’ятати:

код python ds_reference_ukr.py

print('Просте присвоєння')
список_покупок = ['яблуко', 'манго', 'морква', 'банан']
# мій_список — це просто інша назва, що вказує на той самий об’єкт!
мій_список = список_покупок

# Я купив перший товар, тому видаляю його зі списку
del список_покупок[0]

print('список_покупок :', список_покупок)
print('мій_список :', мій_список)
# Зауважте, що друкуються і список_покупок,і мій_список
# з однаковим списком без пункта «яблука»
# вони вказують на той самий об'єкт

print('Копіювати, зробивши повний зріз')
# Зробіть копію, зробивши повний зріз
мій_список = список_покупок[:]
# Видалити перший елемент
del мій_список[0]

print('список_покупок :', список_покупок)
print('мій_список :', мій_список)
# Зверніть увагу, що тепер два списки різні

Висновок:

$ python3 ds_reference_ukr.py
Просте присвоєння
список_покупок : ['манго', 'морква', 'банан']
мій_список : ['манго', 'морква', 'банан']
Копіювати, зробивши повний зріз
список_покупок : ['манго', 'морква', 'банан']
мій_список : ['морква', 'банан']

Як це працює

Більшість пояснень доступна в коментарях.

Пам’ятайте, що якщо ви хочете зробити копію списку або подібних типів послідовностей, або складних об’єктів (а не простих об’єктів, таких як цілі числа), тоді ви повинні використовувати операцію зріз, щоб зробити копію. Якщо ви просто присвоїте назву змінної іншій назві, обидві вони «посилатимуться» на той самий об’єкт, і це може спричинити проблеми, якщо ви не будете обережні.

Примітка для програмістів Perl

Пам’ятайте, що оператор присвоєння для списків не створює копію. Ви повинні використовувати операцію зріз, щоб створити копію послідовності.

Докладніше про рядки#

uk-flagанглійська: More About Strings

Ми вже детально обговорювали рядки раніше. Що ще можна про них дізнатися? Ну, чи знаєте ви, що рядки також є об’єктами та мають методи, які роблять усе, від перевірки частини рядка до видалення пробілів? Фактично, ви вже використовували рядковий метод… метод format!

Усі рядки, які ви використовуєте в програмах, є об’єктами класу str. Деякі корисні методи цього класу демонструються в наступному прикладі. Щоб отримати повний список таких методів, перегляньте help(str).

код python ds_str_methods_ukr.py

# Це рядковий об'єкт
ім_я = 'Swaroop'

if ім_я.startswith('Swa'):
    print('Так, рядок починається з "Swa"')

if 'a' in ім_я:
    print('Так, він містить рядок "a"')

if ім_я.find('war') != -1:
    print('Так, він містить рядок "war"')

delimiter = '_*_'
мій_лист = ['Данія', 'Норвегія', 'Швеція', 'Ісланді','Фінляндія' ]
print(delimiter.join(мій_лист))

Висновок:

$ python3 ds_str_methods_ukr.py
Так, рядок починається з "Swa"
Так, він містить рядок "a"
Так, він містить рядок "war"
Данія_*_Норвегія_*_Швеція_*_Ісланді_*_Фінляндія

Як це працює

Тут бачимо відразу кілька методів рядків у дії. Метод startswith використовується, щоб дізнатися, чи починається рядок із заданого рядка. Оператор in використовується, щоб перевірити, чи є певний рядок частиною цього рядка.

Метод find використовується для визначення позиції даного підрядка в рядку; find повертає -1, якщо не вдалося знайти підрядок. Клас str також має чудовий метод для об'єднання(англ. “join”) елементів послідовності з рядком, який діє як роздільник між кожним елементом послідовності, і повертає більший рядок, згенерований із цього.

Резюме#

Ми детально дослідили різноманітні вбудовані структури даних Python. Ці структури даних будуть необхідними для написання програм розумного розміру.

Тепер, коли ми маємо багато основ Python, ми далі побачимо, як розробити та написати реальну програму Python.