Про підручник

Цей електронний підручник знайомить з мовою програмування Python. Навчальний матеріал у ньому систематизований за розділами і є введенням у програмування від основ до поглиблених тем. Розділи включають у себе: теоретичні відомості з практичними прикладами, контрольні запитання, вправи і задачі для самостійного виконання.

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

Підручник стане у нагоді всім, хто зацікавлений у вивченні популярної мови програмування Python, незалежно від того, чи вивчали ви інші мови програмування раніше.

1. Вступ

1.1. Історична довідка

Гвідо ван Россум
Гвідо ван Россум
логотип Python

Python – динамічна інтерпретована об’єктно-орієнтована скриптова мова програмування із строгою динамічною типізацією. Розроблена в 1990 році голандським програмістом Гвідо ван Россумом.

Автор назвав мову на честь популярного британського комедійного серіалу 1970-х років «Повітряний цирк Монті Пайтона». Найчастіше вживане прочитання назви мови — «Па́йтон».

Офіційний сайт мови програмування Python.

1.2. Python в реальному світі

1.2.1. Короткий опис

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

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

Програми, написані на мові програмування Python, можуть бути як невеликими скриптами, так і складними системами.

Python абсолютно безкоштовний.

1.2.2. Використання Python

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

Застосування мови програмування Python
  1. BitTorrent – протокол для обміну даними.

  2. Ubuntu Software Center – вільне програмне забезпечення для пошуку, установки і видалення пакетів в системі Ubuntu Linux.

  3. Blender – програма для створення тривимірної комп’ютерної графіки, що включає засоби моделювання, анімації, вимальовування, пост-обробки відео, а також створення відеоігор.

  4. GIMP – растровий графічний редактор, із підтримкою векторної графіки.

  5. World of Tanks.

  6. Вікіпедія.

  7. Google.

  8. DropBox – файловий хостинг, що включає персональне хмарне сховище, синхронізацію файлів і програму-клієнт.

  9. YouTube – популярне відеосховище.

  10. ...

1.2.3. Версії Python

Мови програмування з часом змінюються - розробники додають в них нові можливості, а також виправляють помилки. Зараз доступні дві версії мови: Python 2 і Python 3. Код написаний на Python 2 може некоректно працювати у версії Python 3.

У посібнику використовується версія 3.x. Зокрема, усі приклади коду написані і протестовані для версії Python 3.4. Водночас, використання новіших версій інтерпретатора Python 3 не повинно викликати появу помилок при виконанні прикладів коду.

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

2. Початок роботи

2.1. Python - інтерпретована мова програмування

Python - це високорівнева інтерпретована мова програмування, на відміну від C++, яка є прикладом компільованої мови програмування.

Назва Python відноситься як до мови програмування, так і до інтерпретатора – комп’ютерної програми, яка зчитує вихідний код (написаний на Python) і виконує інструкції (команди).

Програми, написані на мові високого рівня, називаються вихідним кодом. Для виконання програми комп’ютером необхідно перекласти вихідний код на машинну мову, яка складається лише з 0 і 1.

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

  1. Компілятор

  2. Інтерпретатор

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

Інтерпретатор Python починає свою роботу у верхній частині файлу, перекладає перший рядок на машинну мову, а потім виконує його. Цей процес повторюється до кінця файлу.

2.2. Завантаження Python

Версії інтерпретатора Python для різних операційних систем доступні для безкоштовного завантаження за адресою https://www.python.org/downloads:

Офіційний сайт Python
Офіційний сайт Python: різні версії мови Python
Офіційний сайт Python
Офіційний сайт Python: вибір дистрибутива для вашої операційної системи

Як завантажити і налаштувати роботу Python читайте у Додатку А: Завантаження і встановлення Python.

2.3. Середовище програмування для Python

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

Текстові редактори та інтегровані середовища програмування для Python

IDLE — стандартний редактор Python. Встановлюється разом з Python для користувачів Windows, окремим пакетом для користувачів Linux.

Notepad++ - безкоштовний текстовий редактор вихідного коду, який підтримує велику кількість мов, в тому числі і Python. Лише для користувачів Windows.

PyScripter - інтегроване середовище розробки для мови програмування Python, працює під Windows. Поширюється безкоштовно.

Wing IDE 101 - вільне інтегроване середовище для Python, розроблене для навчання програмістів-початківців. Для користувачів Linux, Windows і Mac OS X. Поширюється безкоштовно.

Geany - вільний текстовий редактор з базовими елементами інтегрованого середовища розробки, доступний для операційних систем Linux, Mac OS X і Windows.

Sublime Text 3 - кросплатформенний текстовий редактор вихідних текстів програм та інтегроване середовище розробки. Підтримує плагіни, розроблені за допомогою мови програмування Python. Sublime Text не є вільним чи відкритим програмним забезпеченням, але деякі його плагіни розповсюджуються з вільною ліцензією, розробляються і підтримуються спільнотою розробників. Проте, можна використовувати вільно, хоча часто з’являється повідомлення про придбання ліцензії. Для користувачів Windows, Mac OS X, Linux.

PyCharm — інтегроване середовище розробки для мови програмування Python. Підтримує веб-розробку на Django. PyCharm є власницьким програмним забезпеченням. Присутні безкоштовна версія Community з усіченим набором можливостей і безкоштовна версія Edu для навчальних закладів. PyCharm працює під операційними системами Windows, Mac OS X і Linux.

Для налаштування вищезгаданих середовищ програмування зверніться у Додаток B: Налаштування середовища програмування.

2.4. Запуск Python: інтерактивний інтерпретатор

У режимі інтерактивного інтерпретатора команди вводяться у термінальному вікні одна за одною і по натисненні клавіші Enter відразу виконуються з відображенням результату виконання.

Для переходу в цей режим:

  • натисніть сполучення клавіш Win+R на клавіатурі, введіть команду cmd, натисніть OK (для користувачів Windows)

  • натиcніть сполучення клавіш Ctrl+Alt+T (для користувачів Linux)

У термінальному вікні, що з’явилося, введіть команду (у випадку використання Windows):

python

або (у випадку використання Linux (Ubuntu) можна вказати версію Python, наприклад, 3.6):

python3.6

Якщо на екрані з’явиться запрошення >>>, значить система виявила встановлену версію Python:

Режим інтерактивного інтерпретатора Python у термінальному вікні Windows
Режим інтерактивного інтерпретатора Python у термінальному вікні Windows: запрошення до введення команд
Режим інтерактивного інтерпретатора Python у термінальному вікні Linux (Ubuntu)
Режим інтерактивного інтерпретатора Python у термінальному вікні Linux (Ubuntu): запрошення до введення команд

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

print('Hello, Python!')

натисніть Enter і переконайтеся в тому, що на екрані з’явилось повідомлення Hello, Python!:

Режим інтерактивного інтерпретатора Python у термінальному вікні Windows
Режим інтерактивного інтерпретатора Python у термінальному вікні Windows: виведення текстового повідомлення
Функція print() входить у стандартну бібілотеку Python. Вона виводить інформацію, вказану в дужках, на екран або записує у файл.

Щоб закрити термінальний режим інтерпретатора Python, натисніть Ctrl+Z або введіть команду exit().

2.5. Робота з файлами Python

Програми, написані на мові Python, зберігають у вигляді текстових файлів з розширенням .py. В текстовому редакторі створіть новий файл і введіть у нього рядок:

print("Hello, Python!")

Збережіть файл з іменем hello.py у папку python_work, яка, наприклад, знаходиться на робочому столі вашої операційної системи.

Переконайтеся, що ви зберегли файл як простий текст!

Такі файли з кодом програми можна запускати у термінальному вікні, ввівши python і імена цих файлів.

2.5.1. Запуск програм в терміналі Windows

Відкрийте термінальне вікно (вікно командного рядка): натисніть сполучення клавіш Win+R на клавіатурі, введіть команду cmd, натисніть OK і виконайте команди:

C:\Users>User> cd Desktop (1)
C:\Users>User\Desktop> cd python_work (2)
C:\Users>User\Desktop\python_work> dir (3)
hello.py (4)
C:\Users>User\Desktop\python_work> python hello.py (5)
Hello, Python! (6)
Назва User в записі - це ім’я користувача в системі.
  1. Переходимо у папку Desktop (Робочий стіл) з використанням команди cd.

  2. Переходимо у папку python_work з використанням команди cd.

  3. Читаємо вміст папки python_work з використанням команди dir.

  4. Відображення вмісту (єдиний файл hello.py).

  5. Запускаємо на виконання файл hello.py (ввести python та ім’я файлу).

  6. Відображення результату у вікні терміналу.

Для переходу на рівень вгору у дереві папок використовують інструкцію cd ..

Щоб закрити вікно терміналу, введіть команду exit.

2.5.2. Запуск програм в терміналі Linux (Ubuntu)

Відкрийте вікно терміналу, натиснувши сполучення клавіш Ctrl+Alt+T, і виконайте команди:

gt@gt-VirtualBox:~$ pwd (1)
/home/gt
gt@gt-VirtualBox:~$ ls (2)
Видиво     Завантаження      Картинки  Стільниця  examples.desktop
Документи  Загальнодоступні  Музика    Шаблони
gt@gt-VirtualBox:~$ cd Стільниця (3)
gt@gt-VirtualBox:~/Стільниця$
gt@gt-VirtualBox:~/Стільниця$ ls (4)
python_work
gt@gt-VirtualBox:~/Стільниця$ cd python_work (5)
gt@gt-VirtualBox:~/Стільниця/python_work$ ls (6)
hello.py
gt@gt-VirtualBox:~/Стільниця/python_work$ python hello.py (7)
Hello, World! (8)
gt@gt-VirtualBox:~/Стільниця/python_work$ cd .. (9)
gt@gt-VirtualBox:~/Стільниця$ cd ~ (10)
gt@gt-VirtualBox:~$
  1. Дізнаємося поточне розташування з використанням команди pwd (після запуску терміналу поточна папка - домашня папка користувача).

  2. Читаємо вміст поточної папки gt з використанням команди ls.

  3. Переходимо у папку Стільниця з використанням команди cd.

  4. Читаємо вміст папки Стільниця (всередині папка python_work).

  5. Переходимо у папку python_work з використанням команди cd.

  6. Читаємо вміст папки python_work (всередині файл hello.py).

  7. Запускаємо на виконання файл hello.py (ввести python та ім’я файлу).

  8. Відображення результату у вікні терміналу.

  9. Переходимо на рівень вгору в дереві папок з використанням інструкції cd ...

  10. Відразу переходимо у домашню папку користувача з використанням інструкції cd ˜.

Назва gt@gt-VirtualBox у записі - це ім’я користувача в системі і назва комп’ютера, розділені символом @.

Щоб закрити вікно терміналу, введіть команду exit.

2.6. Повідомлення про помилку

В процесі написання і виконання програм можуть з’являтися різноманітні помилки. У таких випадках інтерпретатор Python сам сигналізує про помилку. Наприклад, коли ми введемо в режимі інтерактивного інтерпретатора інструкцію '19' + 81, з’явиться таке повідомлення:

>>> '19' + 81
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly

Введена інструкція некоректна для Python, тому він вказав назву помилки і номер рядка, в якому вона виникла, зупинивши виконання програми.

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

2.7. Коментарі

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

У мові Python ознакою коментаря є символ #. Інтерпретатор Python ігнорує всі символи в коді після # до кінця рядка. Наприклад:

>>> # Привіт, світ!
... print("Hello, world!")
Hello, world!

2.8. Продовження рядків

Будь-яка програма стає більш зрозумілою, якщо її рядки короткі. Рекомендована (але не обов’язкова) максимальна довжина рядка дорівнює 80 символам. Якщо ви не можете висловити свою думку в рамках 80 символів, скористайтеся символом продовження рядка (\). Просто помістіть \ в кінець рядка, а Python буде діяти так, ніби це все той самий рядок. Наприклад:

>>> alphabet = 'abcdefg' + \
... 'hijklmnop' + \
... 'qrstuv' + \
... 'wxyz'
>>> alphabet
'abcdefghijklmnopqrstuvwxyz'
>>> 1 + 2 + \
... 3
6

2.9. Стиль Python

Кожна мова програмування має свій стиль і Python не є винятком. Розробники мови є прихильниками певної філософії програмування, яку називають «The Zen of Python» («Дзен Пайтона»). Її текст можна отримати у інтерпретаторі Python за допомогою команди:

import this

У більшості мов програмування використовуються спеціальні символи (наприклад, фігурні дужки {}) або ключові слова (наприклад, begin і end) для того, щоб розбити код на частини. У цих мовах хорошим тоном є використання відступів при написанні коду, щоб зробити програму зручною для читання як для себе, так і для інших.

Гвідо ван Россум при розробці Python вирішив, що виділення пропусками (пробілами) буде достатньо, щоб задавати структуру програми і уникнути введення всіх цих дужок або ключових слів.

У Python пропуски (пробіли) використовуються для того, щоб будувати структуру програми у вигляді відступів від лівого краю.

Наприклад, (крапками позначені пробіли):

n = int(input())
out = []
for i in range(n):
....k = 0
....while k < i + 1:
........out.append(i+1)
........k += 1
....if len(out) > n:
........break
out = out[0:n]
for i in out:
....print(i, end = " ")

Крім того, для Python визначені рекомендації по стилю написання коду PEP8.

2.10. Завдання

2.10.1. Контрольні запитання

  1. Хто є розробником мови Python? Звідки походить назва мови програмування Python?

  2. Для яких цілей використовується Python?

  3. Як перейти в режим інтерактивного інтерпретатора?

  4. Яке розширення мають файли із програмами, написаними на мові Python?

  5. Як запустити програму, що міститься у файлі, на виконання у термінальному вікні?

  6. У яких випадках, під час виконання програми, може з’явитися повідомлення Traceback (most recent call last)?

  7. Який символ використовується у мові Python для позначення коментарів?

2.10.2. Вправи

  1. Встановіть Python 3. Зверніться до Додатку А: Завантаження і встановлення Python.

  2. Встановіть середовище програмування. Зверніться до Додатку B: Налаштування середовища програмування.

  3. Запустіть інтерактивний інтерпретатор Python 3 і використайте його як калькулятор. Наприклад, обчисліть 19 * 81. Запишіть цей добуток і натисніть Enter, щоб побачити результат. Python повинен вивести 1539.

  4. Введіть число 43 і натисніть клавішу Enter. Чи з’явилось це число в наступному рядку?

  5. Введіть print(43) і натисніть клавішу Enter. Чи з’явилось знову це число в наступному рядку?

  6. В інтерактивному інтерпретаторі Python введіть 'Python' + 3. Знайдіть інформацію в мережі Інтернет про помилку, що виникла, за її назвою.

  7. Перегляньте принципи Python, ввівши в термінальму сеансі команду import this. Знайдіть переклад тексту філософії Python на сторінці.

3. Прості типи даних

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

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

Імена дозволяють ідентифікувати дані, тобто відрізняти їх між собою. Тип обирають для кожної величини, яка використовується для подання реальних об’єктів.

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

У Python вбудовані такі прості типи даних:

  1. Булеві значення (які мають значення True чи False).

  2. Цілі числа (на зразок 81 або 1 000 000).

  3. Числа з плаваючою комою (на зразок 3,14159 або 1.0е6, що означає «один помножити на десять у шостому ступені», або 1 000 000,0).

  4. Рядки (послідовності текстових символів).

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

Кожен тип має специфічні правила використання і вони по-різному обробляються комп’ютером.

3.1. Об’єкти та змінні

В Python все - булеві значення, цілі числа, числа з плаваючою точкою, рядки, складні структури даних, функції - реалізовано як об’єкти.

Що таке об’єкт?

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

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

Точно так само і в Python - якщо об’єкт має тип цілих чисел, ви знаєте, що зможете скласти його з іншим об’єктом, який має такий самий тип цілих чисел.

Тип також визначає, чи можна змінити значення, яке зберігається в скриньці (змінюване значення), або воно є константою (незмінне значення).

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

Мови програмування також дозволяють визначати змінні. Ви можете визначити їх для використання у своїй програмі.

В Python для присвоювання змінній певного значення використовується символ =.

У математиці символ = означає «дорівнює». У багатьох мовах програмування, включаючи Python, цей символ використовується для позначення «присвоювання».

У наступному фрагменті програми ціле число 12 присвоюється змінній з ім’ям a, потім на екран виводиться значення, пов’язане в поточний момент з цією змінною:

>>> a = 12
>>> a
12
В Python змінні - це просто імена. Присвоювання не копіює значення, воно прикріплює ім’я до об’єкта, який містить дані. Ім’я - це посилання на якийсь об’єкт, а не сам об’єкт. Ім’я можна розглядати як стікер-наклейку.
Ціле число 12 присвоюється змінній з ім’ям a
Ціле число 12 присвоюється змінній з ім'ям a: змінна a - це посилання на об’єкт (скриньку для цілих значень), який містить значення 12

За допомогою інтерактивного інтерпретатора виконайте наступні кроки:

  1. Як і раніше, надайте значення 12 змінній a. Це створить об’єкт-«скриньку», який міститиме цілочисельне значення 12.

  2. Виведіть на екран значення a.

  3. Присвойте a змінній b, змусивши b прикріпитися до об’єкту-«скриньки», який містить значення 12.

  4. Виведіть значення b.

>>> a = 12
>>> a
12
>>> b = a
>>> b
12

Що зберігатиметься у змінній b, якщо записати наступні дві інструкції?

>>> b = 'Goodbye!'
>>> b = 'Hello!'
>>> b
'Hello!'
Переприсвоювання значень для однієї змінної
Коли змінній присвоюється нове значення, попереднє значення втрачається: тепер значення змінної b дорівнює 'Hello!'

В Python, якщо ви хочете дізнатися тип якогось об’єкта (змінної або значення), вам слід використовувати конструкцію type(об’єкт). Спробуємо зробити це для різних значень (81, 99.99, ruby) і змінних (a, b):

>>> type(a)
<Class 'int'>
>>> type(b)
<Class 'int'>
>>> type(81)
<Class 'int'>
>>> type(99.99)
<Class 'float'>
>>> type('ruby')
<Class 'str'>
Клас - це визначення об’єкта. В Python значення термінів «клас» і «тип» приблизно однакові.

Необхідно дотримуватись певних вимог використання імен змінних. Імена змінних можуть містити тільки такі символи:

  • літери в нижньому регістрі (від «a» до «z»)

  • літери у верхньому регістрі (від «A» до «Z»)

  • цифри (від 0 до 9)

  • нижнє підкреслення (_)

Імена не можуть починатися з цифри.

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

Коректними є такі імена:

a
a5
a_b_c_81
abc
_3a

Наступні імена є некоректними:

2
2a
2_

Не слід використовувати наступні слова, для імен змінних, оскільки вони є зарезервованими словами Python:

Таблиця "Зарезервовані слова Python"

False

class

finally

is

return

None

continue

for

lambda

try

True

def

from

nonlocal

while

and

del

global

not

with

as

elif

if

or

yield

assert

else

import

pass

break

Ці слова і деякі знаки пунктуації використовуються у синтаксисі мови Python.

3.2. Числа

Числа дуже часто застосовуються у програмуванні (ведення рахунку у грі, представлення даних на діаграмі, зберігання інформації у веб-додатках тощо). Числа використовуються у виразах (наприклад, 2 + 3), у яких виконують обчислення за допомогою математичних операторів (наприклад, +) над операндами (наприклад, 2, 3).

Таблиця "Математичні оператори та їх використання"
Оператор Опис Приклад Результат

+

Додавання

5 + 7

12

-

Віднімання

55 - 10

45

*

Множення

4 * 6

24

/

Ділення

7 / 2

3.5

//

Цілочисельне ділення

7 // 2

3

%

Остача від ділення

7 % 3

1

**

Піднесення до степеня

3 ** 4

81

В Python числові дані поділяються на кілька категорій за способом їх використання.

3.2.1. Цілі числа

Будь-яка послідовність цифр в Python вважається цілим числом:

Не потрібно ставити на початку числа 0, бо це викличе помилку некоректний символ.
>>> 10
10
>>> 05
  File "<stdin>", line 1
    05
     ^
SyntaxError: invalid token

Послідовність цифр вказує на ціле число. Якщо ви поставите знак + перед цифрами, число залишиться незмінним:

>>> 132
132
>>> +132
132

Щоб вказати на від’ємне число, поставте перед цифрами знак -:

>>> -321
-321

За допомогою Python можна виконувати арифметичні дії як зі звичайним калькулятором:

>>> 25 + 9
34
>>> 145 - 37
108
>>> 8 + 3 - 2 + 1 - 106
-96
>>> 6 * 7
42

Операції ділення існує два види. За допомогою оператора / виконується ділення з плаваючою точкою (десяткове ділення). Навіть, якщо ви ділите ціле число на ціле число, оператор / дасть результат з плаваючою точкою:

>>> 9 / 5
1.8

Цілочисельне ділення за допомогою оператора // дає цілочисельну відповідь, відкидаючи залишок:

>>> 9 // 5
1

Ділення на нуль з допомогою будь-якого оператора викличе помилку:

>>> 6 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 8 // 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

Використаємо в обчисленнях змінні та змінимо їх значення:

>>> a = 42
>>> a = a - 2
>>> a
40
В Python вираз, який стоїть справа від знака присвоювання =, обчислюється в першу чергу, запам’ятовується результат обчислення і тільки потім результат обчислення присвоюється змінній, яка стоїть з лівої сторони.

Арифметичні оператори можуть використовуватися разом із оператором присвоювання, розміщуючи їх перед символом присвоювання:

>>> a = 95
>>> a -= 3 (1)
>>> a
92
>>> a += 8 (2)
>>> a
100
>>> a *= 2 (3)
>>> a
200
>>> a /= 3 (4)
>>> a
66.66666666666667
  1. Аналогічно виразу a = a - 3.

  2. Аналогічно виразу a = a + 8.

  3. Аналогічно виразу a = a * 2.

  4. Аналогічно виразу a = a / 3.

За допомогою символу %, коли він знаходиться між двома числами, обчислюється остача від ділення першого числа на друге:

>>> 23 % 6
5

3.2.2. Перетворення типів: функція int()

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

Функція int() зберігає цілу частину числа і відкидає будь-який залишок.

Найпростіший тип даних в Python - булеві змінні, значеннями цього типу можуть бути тільки True або False. При перетворенні в цілі числа вони набувають значень 1 і 0:

>>> int(True)
1
>>> int(False)
0

Перетворення числа з плаваючою точкою в ціле число просто відсікає все, що знаходиться після десяткової точки:

>>> int(98.6)
98
>>> int(1.5e4)
15000

Розглянемо приклад перетворення текстового рядка, який містить тільки цифри або цифри і знаки + і -:

>>> int('99')
99
>>> int('-23')
-23
>>> int('+12')
12

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

>>> int('22 abc')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '22 abc'

3.2.3. Пріоритет операторів

Для ознайомлення з таблицею пріоритетів операторів Python перейдіть на сайт з документацією.

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

>>> 7 * (3 + 4)
49

3.2.4. Числа з плаваючою точкою

В Python числа, що мають дробову частину, називаються дійсними (або «числами з плаваючою точкою»). Числа з плаваючою точкою обробляються так само, як і цілі: можна використовувати оператори +, -, *, /, //, **, %.

3.2.5. Перетворення типів: функція float()

Для того щоб перетворити інші типи в тип float (так називають дійсні числа в Python), cлід використовувати функцію float().

Функція float() перетворює значення інших типів у значення з плаваючою точкою.

Булеві значення обробляються як невеликі числа:

>>> float(True)
1.0
>>> float(False)
0.0

Перетворення значення типу int в тип float:

>>> float(98)
98.0

Ви також можете перетворювати рядки, що містять символи, які є коректним числами (цілими або з плаваючою точкою):

>>> float('99')
99.0
>>> float ('98.6')
98.6
>>> float ('-1.5')
-1.5
>>> float ('1.0e4')
10000.0

3.2.6. Математичні функції

Python має багато математичних функцій стандартної бібліотеки math. Щоб отримати до них доступ зі своїх програм, необхідно ввести import math.

Ця бібліотека містить такі константи, як pi (число Пі) і e (експонента):

>>> import math
>>> math.pi
3.141592653589793
>>> math.e
2.718281828459045

Розглянемо кілька корисних функцій бібліотеки math:

>>> math.fabs(-221.1) (1)
221.1
>>> math.floor(56.8) (2)
56
>>> math.ceil(38.3) (3)
39
>>> math.factorial(7) (4)
5040
>>> math.pow(4, 3) (5)
64.0
>>> math.sqrt(256) (6)
16.0
>>> math.radians(180) (7)
3.141592653589793
>>> math.degrees(math.pi) (8)
180.0
  1. Повертає абсолютне значення.

  2. Округлення вниз.

  3. Округлення вгору.

  4. Обчислення факторіалу.

  5. Піднесення числа до степеня.

  6. Обчислення кореня квадратного з числа.

  7. Перетворення значення в градусах у радіани.

  8. Перетворення значення в радіанах у градусну міру.

В цій бібліотеці також присутні тригонометричні функції: sin(), cos(), tan(), asin(), acos(), atan().

3.3. Рядки

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

Завдяки підтримці стандарту Unicode Python 3 може містити символи будь-якої мови світу, а також багато інших символів. Необхідність роботи із стандартом Unicode була однією з причин зміни Python 2.
В усіх наступних прикладах підручника використовуватимуться рядки формату ASCII.

3.3.1. Створення рядків і функція print()

Будь-яка послідовність символів, укладена в лапки, в Python вважається рядком, при цьому рядки можуть бути укладені як в одинарні, так і в подвійні лапки:

>>> 'This is a string.'
'This is a string.'
>>> "This is also a string."
'This is also a string.'

Навіщо мати два види лапок? Основна ідея полягає в тому, що ви можете створювати рядки, що містять лапки або апостроф. Тобто, усередині одинарних лапок можна розташувати подвійні і навпаки:

>>> 'I told my friend, "Python is my favorite language!"'
'I told my friend, "Python is my favorite language!"'
>>> "The language 'Python' is named after Monty Python, not the snake."
"The language 'Python' is named after Monty Python, not the snake."
>>> "One of Python's strengths is its diverse and supportive community."
"One of Python's strengths is its diverse and supportive community."

Можна також використовувати три одинарні (''') або три подвійні лапки ("""):

>>> '''Yes'''
'Yes'
>>> """No"""
'No'

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

>>> """In the age of high technology
... Without programs you can not do,
... Programmers daily
... It makes life easier for us!"""

Кожен рядок вірша вводився в інтерактивний інтерпретатор до тих пір, поки ми не ввели останні потрійні лапки і не перейшли до наступного рядка.

Усередині потрійних лапок символи переходу на новий рядок (\n), пропуски зберігаються.

Якби ви спробували створити вірш за допомогою одинарних лапок, Python згенерував би помилку, коли б ви перейшли до наступного рядка:

>>> 'In the age of high technology
  File "<stdin>", line 1
    'In the age of high technology
                                 ^
SyntaxError: EOL while scanning string literal

Існує різниця між результатом виведення на екран за допомогою інтерактивного інтерпретатора (на зразок рядків вірша)

>>> """In the age of high technology
... Without programs you can not do,
... Programmers daily
... It makes life easier for us!"""
'In the age of high technology\nWithout programs you can not do,\nProgrammers daily\nIt makes life easier for us!'

та функції print():

>>> print('Python', 'is', 'my', 'favorite', 'language!')
Python is my favorite language!

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

Функція print() має необов’язкові параметри end і sep, за допомогою яких можна вказати відповідно текст, який повинен бути в кінці виведення функції, і текст, який повинен бути розділювачем усередині вмісту функції.

Щоб змінити розділювач (за замовчуванянм, це пропуск) між елементами, які виводить функція print(), треба використати її необов’язковий параметр sep і вказати розділювач:

>>> print('Python', 'is', 'my', 'favorite', 'language!', sep=',')
Python,is,my,favorite,language!

Поглянемо на використання необов’язкового параметра end функції print(). Для прикладу, запустимо наступну програму, яка збережена у окремому файлі з певним ім’ям:

print('Hello')
print('World')

Результат виведення буде таким:

Hello
World

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

Наприклад для програми:

print('Hello', end='')
print('World')

виведення буде таким:

HelloWorld

В даному випадку текст виводиться в один рядок, тому що відсутній символ нового рядка \n після рядка 'Hello'. Замість символа нового рядка \n виводиться порожній рядок ''. Цей прийом буде в нагоді в тих випадках, коли необхідно відмінити використання символа нового рядка \n, який автоматично додається в кінці кожного виведення за допомогою функції print().

Згаданий вище, порожній рядок можна утворити за допомогою різних лапок:

>>> ''
''
>>> ""
''
>>> """"""
''
>>> ''''''
''

Необхідність створення порожніх рядків виникає, наприклад, при утворенні рядків з інших рядків:

>>> score = 55
>>> base = ''
>>> base += 'checking account: '
>>> base += str(score)
>>> base
'checking account: 55'

3.3.2. Стиль форматування: функція format()

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

Визначимо кілька змінних: цілочисельну n, число з плаваючою точкою f і рядок s:

>>> n = 25
>>> f = 9.03
>>> s = 'search string'

За допомогою функції format() можна певним чином розмістити ці значення в одному рядку у тому порядку, як вони вказані для функції:

>>> '{} {} {}'.format(n, f, s)
'25 9.03 search string'

Порядок виведення значень в рядку можна вказувати у фігурних дужках таким чином (нумерація починається з нуля):

>>> '{2} {0} {1}'.format(n, f, s)
'search string 25 9.03'

У поданих прикладах значення виводяться на екран з форматуванням за замовчуванням. Python дозволяє вказувати специфікатори типу після символу : для форматування. Для прикладу:

>>> '{0:d} {1:f} {2:s}'.format(n, f, s)
'25 7.030000 search string'
Таблиця "Специфікатори типів"
Позначення Тип

s

Рядок

d

Ціле число в десятковій системі числення

f

Число з плаваючою точкою у десятковій системі числення

Якщо потрібно вивести число з плаваючою точкою з точністю, наприклад, до трьох знаків, можна використати запис .3 перед ім’ям специфікатора f:

>>> '{0:d} {1:.3f} {2:s}'.format(n, f, s)
'25 9.030 search string'

Функцією format() підтримуються і інші можливості (мінімальна довжина поля, максимальна ширина символів, зміщення і т. д.). Ось кілька прикладів:

Мінімальна довжина поля становить 10 для кожного значення, вирівнювання по правому краю (за замовчуванням):

>>> '{0:10d} {1:10f} {2:10s}'.format(n, f, s)
'        25   9.030000 search string'

Мінімальна довжина поля становить 20 для кожного значення, вирівнювання по правому краю:

>>> '{0:>20d} {1:>20f} {2:>20s}'.format(n, f, s)
'                  25             9.030000        search string'

Мінімальна довжина поля становить 15 для кожного значення, вирівнювання по лівому краю:

>>> '{0:<15d} {1:<15f} {2:<15s}'.format(n, f, s)
'25              9.030000        search string  '

Мінімальна довжина поля становить 10 для кожного значення, вирівнювання по центру:

>>> '{0:^10d} {1:^10f} {2:^10s}'.format(n, f, s)
'    25      9.030000  search string'

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

>>> '{0:=^21s}'.format('My web-page')
'=====My web-page====='

3.3.3. Перетворення типів: функція str()

Ви можете перетворювати інші типи даних Python в рядки за допомогою функції str():

>>> str(98.6)
'98.6'
>>> str(25)
'25'
>>> str(1.0e4)
'10000.0'
>>> str(True)
'True'

3.3.4. Керуючі символи

Python дозволяє створювати керуючі послідовності всередині рядків. Найбільш поширена послідовність \n, яка означає перехід на новий рядок. З її допомогою можна створити багаторядкові рядки з однорядкових:

>>> print('Languages:\nPython\nC\nRuby')
Languages:
Python
C
Ruby

Часто використовують послідовність \t (табуляція), яка використовується для вирівнювання тексту, завдяки відступам:

>>> print('\tasciidoctor')
	asciidoctor
>>> print('ascii\tdoctor')
ascii	doctor

Послідовності \' або \" використовуються, щоб помістити в рядок одинарні або подвійні лапки, які оточені таким ж лапками:

>>> print("The language \"Python\" is named after Monty Python, not the snake.")
The language "Python" is named after Monty Python, not the snake.
>>> print('One of Python\'s strengths is its diverse and supportive community.')
One of Python's strengths is its diverse and supportive community.

А якщо потрібно ввести зворотний слеш (\), перед ним треба поставити ще один:

>>> print('This inverse slash \\')
This inverse slash \

3.3.5. Конкатенація рядків

В Python за допомогою оператора + можна об’єднати рядки або рядкові змінні. Уявіть, що ім’я та прізвище зберігаються в різних змінних і ви хочете об’єднати їх для виведення повного імені:

>>> first_name = "Ada"
>>> last_name = "Lovelace"
>>> full_name = first_name + " " + last_name
>>> print(full_name)
Ada Lovelace

Такий спосіб об’єднання рядків називається конкатенацією.

При конкатенації рядків Python не додає пропуски. Їх потрібно явно додавати.

Часто об’єкти, які не є рядками, використовується всередині рядків. Припустимо, ви хочете привітати свого товариша з днем народження. Ви написали для цього наступний код і запустили на його виконання:

>>> age = 23
>>> message = "Happy " + age + "rd Birthday!"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly

Виникла помилка невідповідність типу. При такому використанні цілих чисел в рядках необхідно явно вказати, що ціле число повинне використовуватися як рядок із символів. Для цього змінна передається функції str(), яка перетворює нерядкові значення в рядкові:

>>> age = 23
>>> message = "Happy " + str(age) + "rd Birthday!"
>>> print(message)
Happy 23rd Birthday!

3.3.6. Дублювання рядків

Оператор * (зірочка) можна використовувати для того, щоб продублювати рядок. Спробуйте ввести в інтерактивний інтерпретатор наступні рядки і проаналізувати результат:

>>> start = 'Na' * 4 + '\n'
>>> middle = 'Hey ' * 3 + '\n'
>>> end = 'Goodbye.'
>>> print(start + start + middle + end)
NaNaNaNa
NaNaNaNa
Hey Hey Hey
Goodbye.

3.3.7. Доступ до елемента рядка по індексу

Для того, щоб отримати один символ рядка, задайте індекс (місцезнаходження) цього символа у рядку всередині квадратних дужок після імені рядка:

>>> letters = 'abcdefghijklmnopqrstuvwxyz'
>>> letters[0]
'a'
>>> letters[6]
'g'
>>> letters[-1]
'z'
>>> letters[36]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range
Індекси мають значення в діапазоні від 0 до довжина рядка - 1.

3.3.8. Зрізи: функція slice[start: end: step]

З рядка можна «витягти» не лише один символ, але й підрядок (частину рядка) за допомогою функції slice. У квадратних дужках запису цієї функції вказується:

  • індекс початку підрядка start

  • індекс кінця підрядка end

  • розміру кроку step

Деякими з цих параметрів можна знехтувати. У підрядок будуть включені символи, розташовані починаючи з символа, на який вказує індекс start, і закінчуватися символом, на який вказує індекс end. Наприклад, дано рядок, що містить англійські букви в нижньому регістрі:

>>> letters = 'abcdefghijklmnopqrstuvwxyz'
>>> letters[:] (1)
'abcdefghijklmnopqrstuvwxyz'
>>> letters[20:] (2)
'uvwxyz'
>>> letters[12:15] (3)
'mno'
>>> letters[-5:] (4)
'vwxyz'
>>> letters[18:-3] (5)
'stuvw'
>>> letters[-6:-2] (6)
'uvwx'
>>> letters[::7] (7)
'ahov'
>>> letters[4:20:3] (8)
'ehknqt'
>>> letters[19::4] (9)
'tx'
>>> letters[:21:5] (10)
'afkpu'
>>> letters[::-1] (11)
'zyxwvutsrqponmlkjihgfedcba'
>>> letters[60:70] (12)
''
  1. Отримати послідовність символів від початку і до кінця.

  2. Отримати послідовність символів від 20-го символа і до кінця.

  3. Отримати послідовність символів від 12-го символа по 15-й (15-й символ не включається).

  4. Отримати послідовність останніх 5 символів.

  5. Отримати послідовність символів від 18-го символа і до 3-го з кінця (3-й символ з кінця не включається).

  6. Отримати послідовність символів від 6-го символа з кінця і до 2-го символа з кінця (2-й символ з кінця не включається).

  7. Отримати кожний 7-й символ з початку і до кінця.

  8. Отримати кожний 3-й символ від 4-го символа з початку і до 20-го символа (20-й символ не включається).

  9. Отримати кожний 4-й символ починаючи від 19-го символа і до кінця.

  10. Отримати кожний 5-й символ починаючи з початку і до 21-го символа (21-й символ не включається).

  11. Перевернути рядок з кінця на початок.

  12. Вказування неправильних початкового і кінцевого індексів дає порожній рядок.

3.3.9. Довжина рядка: функція len()

Функція len() підраховує символи в рядку:

>>> len(letters)
26
>>> empty = ""
>>> len(empty)
0
Функцію len() можна використовувати і для інших типів послідовностей.

3.3.10. Розділення рядка: функція split()

Функція split() розбиває рядок на окремі рядки і розміщує їх у списку.

Використовуючи цю функції, необхідно вказати символ-розділювач (у нашому прикладі ,):

>>> dolist = 'pass algorithm, write a program, test program'
>>> dolist.split(',')
['pass algorithm', ' write a program', ' test program']

Якщо ви не вкажете символ-розділювач, функція split() буде використовувати будь-яку послідовність пропусків, а також символи нового рядка і табуляцію:

>>> dolist.split()
['pass', 'algorithm,', 'write', 'a', 'program,', 'test', 'program']
Для виклику функції split(), навіть без аргументів, все одно потрібно додавати круглі дужки - саме так Python дізнається, що викликається функція.

3.3.11. Об’єднання рядків: функція join()

Функція join() є протилежністю функції split().

Функція join() об’єднує список рядків в один рядок.

Виклик функції виглядає трохи заплутано, оскільки спочатку ви вказуєте рядок, який об’єднує інші, а потім - список рядків для об’єднання: рядок.join(список рядків). В наступному прикладі об’єднаємо кілька імен в список, розділений комами і пропусками:

>>> crypto_list = ['Jumanji: Welcome to the Jungle', 'The Lost City of Z', 'Justice League']
>>> crypto_string = ', '.join(crypto_list)
>>> print('Browse films:', crypto_string)
Browse films: Jumanji: Welcome to the Jungle, The Lost City of Z, Justice League

3.3.12. Регістр і вирівнювання

Розглянемо ще кілька прикладів використання вбудованих у Python функцій для роботи з рядками. Створимо рядкову змінну з таким вмістом:

>>> setup = 'beetles buzzing over cherries...'

Видалимо символи . з обох кінців рядка за допомогою функції strip():

Функції rstrip() і lstrip() видаляють пропуски, а також символи нового рядка і табуляцію відповідно в кінці і на початку рядка.
>>> setup.strip('.')
'beetles buzzing over cherries'
>>> setup
'beetles buzzing over cherries...'
Оскільки рядки незмінні, рядок setup не змінюється. Відбувається все так: отримують значення змінної setup, виконують над цим значенням деякі дії, а потім повертають результат як новий рядок.

Напишемо всі слова з великої літери за допомогою функції title():

>>> setup.title()
'Beetles Buzzing Over Cherries...'

Напишемо перше слово з великої літери, а всі інші символи - малими літерами за допомогою функції capitalize():

>>> setup.capitalize()
'Beetles buzzing over cherries...'

Напишемо всі слова великими літерами за допомогою функції upper():

>>> setup.upper()
'BEETLES BUZZING OVER CHERRIES...'

Напишемо всі слова малими літерами за допомогою функції lower():

>>> setup.lower()
'beetles buzzing over cherries...'

Змінимо регістр літер за допомогою функції swapcase():

>>> setup = 'Beetles buzzing over Cherries...'
>>> setup.swapcase()
'bEETLES BUZZING OVER cHERRIES...'

Для вирівнювання рядків використовуються кількість місць для рядка і наступні функції: center(), ljust(), rjust().

Вирівнювання рядка по центру на проміжку із 40 місць:

>>> setup.center(40)
'    beetles buzzing over cherries...    '

Вирівнювання рядка за лівим краєм на проміжку із 40 місць:

>>> setup.ljust(40)
'beetles buzzing over cherries...        '

Вирівнювання рядка за правим краєм на проміжку із 40 місць:

>>> setup.rjust(40)
'        beetles buzzing over cherries...'

3.3.13. Заміна символів: функція replace()

Ви можете використовувати функцію replace() для того, щоб замінити один підрядок на інший.

Для цього треба передати у цю функцію «старий підрядок» (який треба замінити), «новий підрядок» (яким треба замінити) і кількість входжень старого підрядка: рядок.replace(старий рядок, новий підрядок, кількість замін).

Якщо не записувати останній аргумент (кількість замін), буде проведена заміна усіх входжень. Наприклад:

>>> language = 'I told my friend, "Python is my favorite language! I love to program in Python!"'
>>> language.replace('Python', 'Ruby')
'I told my friend, "Ruby is my favorite language! I love to program in Ruby!"'

Зробимо заміну символа ! на !!! для 1 і 2 входжень відповідно:

>>> language.replace('!', '!!!', 1)
'I told my friend, "Python is my favorite language!!! I love to program in Python!"'
>>> language.replace('!', '!!!', 2)
'I told my friend, "Python is my favorite language!!! I love to program in Python!!!"'

3.4. Введення даних з клавіатури

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

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

3.4.1. Функція input()

Для отримання вхідних даних у програмах використовується функція input().

Функція input() призупиняє виконання програми і чекає, поки користувач введе деякий текст і натисне Enter. Отримавши введення, Python зберігає його у змінній, щоб було зручніше працювати з ним.

Кожен раз, коли у вашій програмі використовується функція input(), ви повинні включати зрозумілу підказку-текст (наприклад, «Please enter your name: »), яка точно повідомить користувачеві, яку інформацію від нього хочуть отримати. Наприклад, для даного фрагменту коду додайте пробіл в кінці підказки (після двокрапки), щоб відокремити підказку від даних, що вводяться користувачем, і чітко показати, де повинен вводитися текст:

>>> name = input("Please enter your name: ")
Please enter your name: Alex
>>> print("Hello, " + name + "!")
Hello, Alex!

При використанні функції input() Python інтерпретує всі дані, введенні користувачем, як рядок. У наступному сеансі інтерпретатора програма запитує у користувача вік:

>>> age = input("How old are you? ")
How old are you? 25
>>> age
'25'

Користувач вводить число 25, але, коли ми запитуємо у Python значення age, виводиться '25' - введене число в рядковому форматі. Лапки, в яких знаходяться дані, вказують на те, що Python інтерпретує введення як рядок.

Спроба використовувати введені дані як число викликає помилку:

>>> age >= 18
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: str() >= int()

Коли ви намагаєтеся порівняти введені дані з числом, Python видає помилку, тому що не може порівняти рядок з числом: рядок '25', що зберігається в age, порівнюється з числовим значенням 18; відбувається помилка. Проблему можна вирішити за допомогою функції int(), яка інтерпретує рядок як числове значення.

Функція int() перетворює рядкове представлення числа в саме число.
>>> age = input("How old are you? ")
How old are you? 25
>>> age = int(age)
>>> age >= 18
True

У цьому прикладі введене число 25 інтерпретується як рядок, але потім він перетвориться в числове представлення викликом int(). Тепер Python може перевірити умову: порівняти змінну age (яка тепер містить числове значення 25) з 18. Умова «значення age більше або дорівнює 18» виконується, і результат перевірки дорівнює True.

3.5. Завдання

3.5.1. Контрольні запитання

  1. Які з наведених нижче елементів є операторами, а які - значеннями?

*
'Hello'
-45.7
-
/
5
+
  1. Що із наведеного нижче є змінною, а що - рядком?

letter
'letter'
  1. Що буде містити змінна result після виконання наступних інструкцій?

result = 20
result += 5
  1. Якими будуть результати (у місці ...) обчислення двох наступних виразів?

>>> 'snowflake' + 'snowflakesnowflake'
...
>>> 'snowflake' * 3
...
  1. Чому виникає помилка при спробі обчислення поданого нижче виразу? Як виправити цю помилку?

print('I got' + 12 + 'points')
  1. Які будуть результати обчислення таких виразів:

"I'd like to travel to Shanghai"[0:21]
"I'd like to travel to Shanghai"[:21]
"I'd like to travel to Shanghai"[22:]
"I'd like to travel to Shanghai"[12:18]
"I'd like to travel to Shanghai".upper()
"I'd like to travel to Shanghai".upper().lower()
"I'd like to travel to Shanghai".split()
'>>'.join("I'd like to travel to Shanghai".split())

3.5.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

  1. Скільки секунд в годині? Використайте інтерактивний інтерпретатор як калькулятор і помножте кількість секунд у хвилині (60) на кількість хвилин у годині (теж 60).

  2. Присвойте результат обчислення попереднього завдання (кількість секунд в годині) змінній, яка називається seconds_per_hour.

  3. Скільки секунд у добі? Використайте змінну seconds_per_hour.

  4. Знову порахуйте кількість секунд у добі, але на цей раз збережіть результат у змінній seconds_per_day.

  5. Розділіть значення змінної seconds_per_day на значення змінної seconds_per_hour. Використайте ділення з плаваючою точкою (/).

  6. Розділіть значення змінної seconds_per_day на значення змінної seconds_per_hour. Використайте цілочисельне ділення (//). Чи збігається результат з відповіддю на попередню вправу, якщо не враховувати символи .0 в кінці?

  7. Напишіть операції додавання, віднімання, множення і ділення, результатом яких є число 12. Ви повинні написати чотири рядки коду, які виглядають приблизно так: print(5 + 7). Результатом повинні бути чотири рядки, у кожному з яких виводиться число 12.

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

  9. Збережіть ім’я користувача у змінній і виведіть його у нижньому регістрі, у верхньому регістрі і зі зміною регістру літер.

  10. Змінна poem містить фрагмент тексту вірша Contra spem spero Лесі Українки:

>>> poem = '''Yes, I'll smile, indeed, through tears and weeping
... Sing my songs where evil holds its sway,
... Hopeless, a steadfast hope forever keeping,
... I shall live! You thoughts of grief, away!'''

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

Отримати перші 16 символів (їх індекси лежать у діапазоні від 0 до 15).

>>> ...
"Yes, I'll smile"

Скільки символів містить цей вірш? (Пропуски і символи нового рядка враховуються.)

>>> ...
178

Чи починається вірш з буквосполучення Yes?

>>> ...
True

Вірш закінчується словосполученням I shall live!?

>>> ...
False

Знайти індекс першого входження символа коми ,.

>>> word = ','
>>> ...
3

Знайти індекс останнього входження символа коми ,.

>>> ...
171

Скільки разів зустрічається символ коми ,?

>>> ...
7

Всі символи вірша є літерами або цифрами?

>>> ...
False
В разі виникненя труднощів у пошуку відповідних функцій для роботи з рядками, скористайтеся відомими назвами функцій: len(), startswith(), endswith(), find(), rfind(), count(), isalnum().

3.5.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

  1. Збережіть будь-яке повідомлення у змінній і виведіть це повідомлення. Потім замініть значення змінної іншим повідомленням і виведіть нове повідомлення. Програму збережіть у файлі, ім’я якої підпорядковується стандартним правилам Python по використанню малих літер і символів підкреслення - наприклад, simple_messages.py.

  2. Збережіть ім’я користувача у змінній і виведіть повідомлення, призначене для конкретної людини. Повідомлення повинно бути, наприклад, таким: "Hello, Sasha, would you like to learn some Python today?".

  3. Знайдіть відому цитату, яка вам подобається. Збережіть ім’я автора вислову у змінній famous_person. Cкладіть повідомлення і збережіть його у новій змінній з ім’ям message. Виведіть своє повідомлення. Результат повинен виглядати приблизно так (включаючи лапки): Albert Einstein once said, "A person who never made a mistake never tried anything new.".

  4. Збережіть ім’я користувача у змінній і додайте на початку і у кінці імені кілька пропусків. Простежте за тим, щоб кожна керуюча послідовність (\t і \n) зустрічалася принаймні один раз. Виведіть ім’я, щоб було видно пропуски на початку і у кінці рядка. Потім виведіть його знову з використанням кожної з функцій видалення пропусків: lstrip(), rstrip() і strip().

  5. Використайте функцію print() для виведення повної домашньої адреси. У першому рядку виведіть власне ім’я та прізвище. У кожному наступному рядку виведіть окремі елементи адреси (країна, індекс, назва населеного пункту, вулиця, номер будинку тощо).

  6. Виконайте переведення одиниць вимірювання відстаней. Значення відстані вказано у метрах. У кожному новому рядку програма виводить значення відстані, представлене у: дюймах, футах, милях, ярдах тощо. Числові дані на екрані мають бути у відформатованому вигляді: два знаки після десяткової точки. Використайте функцію format(). Потрібні значення одиниць вимірювання знайдіть у мережі Інтернет.

  7. Обчисліть тривалість якоїсь події. Припустимо, учнівські канікули тривали кілька днів. На екран треба вивести у відформатованому вигляді (вирівнювання за лівим краєм, ширина поля: 10 знаків) загальну тривалість цієї події у годинах, хвилинах, секундах.

  8. Виконайте перетворення значення температури у градусах Цельсія (C) для інших температурних шкал: Фаренгейта (F) і Кельвіна (K). Програма повинна відображати еквівалентну температуру у градусах Фаренгейта (F = 32 + 9/5 * C). Програма повинна відображати еквівалентну температуру у градусах Кельвіна (K = C + 273,15). Результати потрібно вивести на екран у відформатованому вигляді: з використанням двох знаків після десяткової точки, мінімальною довжиною поля (15), вирівнюванням по центру. Зверніть увагу, у числах для розділення дробової і цілої частин використовують точку.

  9. Виконайте розкладання чотирицифрового цілого числа і виведіть на екран суму цифр у числі. Наприклад, якщо обрали число 6259, то програма повинна вивести на екран повідомлення: 6 + 2 + 5 + 9 = 22. Використайте функцію format() для відображення результату.

  10. За координатами широти і довготи двох точок на Землі у градусах визначте відстань між ними у кілометрах. Нехай (x1, y1) і (x2, y2) є кординатами широти і довготи (у градусах) двох точок на земній поверхні. Відстань між цими точками у кілометрах обчислюється так: відстань = 6371.032 × агссоs(sin(x1) × sin(x2) + cos(x1) × соs(x2) × cos(y1 - y2)). Значення 6371,032 - це середній радіус Землі у кілометрах. Тригонометричні функції Python працюють з радіанами. Як результат, необхідно перетворити значення координат із градусів у радіани перед обчисленням відстані за формулою. Модуль math містить функцію з ім’ям radians, яка переводить градуси у радіани. Переведення можна зробити і за формулою, на зразок x1 = x1 × pi/180, де pi - число Пі. Знайдіть відстань між двома містами Пекін (39.9075000, 116.3972300) і Київ (50.4546600, 30.5238000) і виведіть значення на екран. Значення відстані повинне відображатися у відформатованому вигляді: з використанням трьох знаків після десяткової точки, мінімальною довжиною поля (10), вирівнюванням за правим краєм. На даному сайті знайдіть значення координат у десяткових градусах ще для кількох пар міст на вибір і визначте відстань у кілометрах між ними. Перевірте правильність визначення відстаней між містами, використовуючи один із сервісів вищезгаданого сайту.

  11. Змініть код програм із пунктів 6-9 таким чином, щоб дані у програми могли вводитись користувачем із клавіатури. Для модифікації коду використовуйте функцію input().

  12. Виберіть дві програми з написаних вами і додайте у кожну хоча б один коментар. Якщо ви не знаєте, що написати у коментарях, додайте своє ім’я і поточну дату на початку коду. Потім додайте один коментар з описом того, що робить програма.

4. Списки і кортежі

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

У список можна додати нові елементи, а також видалити або перезаписати існуючі. Одне і те ж значення може зустрічатися в списку кілька разів.

Що таке список? Список можна створити для зберігання букв алфавіту, цифр від 0 до 9 або рядків, наприклад, імен всіх членів вашої сім’ї. У списку можна зберігати будь-яку інформацію, причому дані в списку навіть не зобов’язані бути якось пов’язані один з одним. Так як список, зазвичай, містить більше одного елемента, рекомендується надавати спискам імена у множині: letters, digits, names і т. д.

4.1. Створення списків

У мові Python список позначається квадратними дужками ([]), а елементи списку розділяються комами. Наприклад, так створюється список з назвами моделей автомобілів:

>>> cars = ['Alfa Romeo', 'Volvo', 'Lamborghini', 'BMW', 'Toyota']
>>> print(cars)
['Alfa Romeo', 'Volvo', 'Lamborghini', 'BMW', 'Toyota']

Інші приклади списків:

>>> empty_list = [] (1)
>>> weekdays = ['Monday', 'Friday', 'Wednesday', 'Thursday', 'Friday'] (2)
>>> animals = ['camels', 'bats', 'elephants', 'dolphins', 'bears'] (3)
>>> first_names = ['Mira', 'John', 'Terry', 'John', 'Michael'] (4)
>>> another_empty_list = list() (5)
>>> another_empty_list
[]
  1. Створення порожнього списку з ім’ям empty_list.

  2. Створення списку днів тижня з ім’ям weekdays.

  3. Створення списку тварин з ім’ям animals.

  4. Створення списку імен з назвою first_names.

  5. Створення порожнього списку another_empty_list за допомогою функції list().

Списки у Python можна порівняти із масивами у інших мовах програмування.

Якщо присвоїти один список більш ніж одній змінній, то зміни у списку в одному місці спричинять за собою його зміни в інших, як показано далі:

>>> a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> b = a
>>> b
[1, 2, 3]
>>> a[0] = 'surprise'
>>> a
['surprise', 2, 3]
>>> b
['surprise', 2, 3]
>>> b[0] = 'I hate surprises'
>>> b
['I hate surprises', 2, 3]
>>> a
['I hate surprises', 2, 3]

Значення списку можна скопіювати в незалежний новий список за допомогою одного з наступних способів:

  • функції copy()

  • функції list()

  • розділенням списку за допомогою [:]

Наприклад, оригінальний список буде присвоєний змінній a, а інші списки - b, c, d - будуть копіями списку a:

>>> a = [1, 2, 3]
>>> b = a.copy()
>>> c = list(a)
>>> d = a[:]

b, c, d - це нові об’єкти, що мають свої значення, які не пов’язані з оригінальним списком елементів [1, 2, 3], на який посилається змінна a. Зміни в a не впливають на копії b, c, d:

>>> a[0] = 'lists store any information, not just integer numbers'
>>> a
['lists store any information, not just integer numbers', 2, 3]
>>> b
[1, 2, 3]
>>> c
[1, 2, 3]
>>> d
[1, 2, 3]
>>>
При створенні копій списку, використовуючи функції copy() і list() або конструкції [:], зміни в оригінальному списку не впливають на зміни у списках-копіях; списки-копії є вже новими об’єктами.

4.2. Довжина списку

Функція len() повертає кількість елементів списку.

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

>>> planets = ['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus']
>>> len(planets)
5

4.3. Перетворення типів: функція list()

Функція list() перетворює інші типи даних у список.

У наступному прикладі рядок перетворюється у список, що складається з односимвольних рядків:

>>> list('sun')
['s', 'u', 'n']

Щоб перетворити рядок у список можна також скористатися функцією split(), вказавши рядок-розділювач:

>>> mybirthday = '3/4/1981'
>>> mybirthday.split('/')
['3', '4', '1981']
Функція split() перетворює рядок у список елементів, розділених іншим рядком-розділювачем.

Якщо в рядку міститься кілька входжень рядка-розділювача поспіль, то в якості елемента списку ви отримаєте порожній рядок:

>>> result = 'a|b||c|d|||e'
>>> result.split('|')
['a', 'b', '', 'c', 'd', '', '', 'e']
Для перетворення списка у рядок, використовується функція join().
>>> planets = ['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus']
>>> ', '.join(planets)
'Mercury, Jupiter, Earth, Mars, Venus'

4.4. Доступ до елементів списку

Списки є впорядкованими наборами даних, тому для доступу до будь-якого елементу списку слід повідомити Python позицію (індекс) потрібного елемента. Індекси приймають тільки цілочисельні значення. Щоб звернутися до елементу у списку, вкажіть ім’я списку, за яким слідує індекс елемента в квадратних дужках.

Індекси починаються з 0, а не з 1

Цей принцип зустрічається в більшості мов програмування. У Python цей принцип пояснюється особливостями низькорівневої реалізація операцій зі списками. Якщо ви отримуєте несподівані результати, визначте, чи не припустились ви помилки «зміщення на 1».

Отже, перший елемент списку відповідає індексу 0. Другий елемент списку відповідає індексу 1 і т. д. Наприклад, щоб звернутися до четвертого елементу списку, слід вказати елемент з індексом 3.

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

>>> cars = ['Alfa Romeo', 'Volvo', 'Lamborghini', 'BMW', 'Toyota']
>>> print(cars[1])
Volvo
>>> print(cars[4])
Toyota
>>> print(cars[0])
Alfa Romeo
>>> print(cars[-1])
Toyota
>>> print(cars[-2])
BMW
>>> print(cars[5])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
Якщо необхідно дізнатися індекс елемента у списку за його значенням, використовується функція index().
>>> planets = ['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus']
>>> planets.index('Earth')
2

4.5. Списки списків

Списки можуть містити елементи різних типів, включаючи інші списки. Створимо три списки і помістимо їх в четвертий список:

>>> text_editors = ['Atom', 'Sublime Text']
>>> programming_language = ['Python', 'C', 'JavaScript']
>>> programmers = ['Guido van Rossum', 'Dennis Ritchie', 'Brendan Eich']
>>> all_info = [text_editors, 'code', programming_language, programmers]
>>> all_info
[['Atom', 'Sublime Text'], 'code', ['Python', 'C', 'JavaScript'], ['Guido van Rossum', 'Dennis Ritchie', 'Brendan Eich']]
>>>

Поглянемо на перший і другий елементи новоствореного списку all_info - вони є списками:

>>> all_info[0]
['Atom', 'Sublime Text']
>>> all_info[2]
['Python', 'C', 'JavaScript']

Щоб отримати із списку all_info, наприклад, перший елемент списку programmers, вказується 2 індекса: [3] - вказує на четвертий елемент списку all_info, а [0] - вказує на перший елемент внутрішнього списку programmers:

>>> all_info[3][0]
'Guido van Rossum'

4.6. Зміна елементів списку

На відміну від рядків список можна змінити:

>>> planets = ['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus']
>>> planets[2] = 'Saturn'
>>> planets
['Mercury', 'Jupiter', 'Saturn', 'Mars', 'Venus']

4.7. Розділення списку

Можна витягти зі списку підпослідовність, яка також буде списком, використовуючи розділення списку:

>>> planets = ['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus']
>>> planets[0:3]
['Mercury', 'Jupiter', 'Earth']
>>> planets[::2]
['Mercury', 'Earth', 'Venus']
>>> planets[::-3]
['Venus', 'Jupiter']
>>> planets[::-1]
['Venus', 'Mars', 'Earth', 'Jupiter', 'Mercury']

4.8. Додавання елемента у список

Традиційний спосіб додавання елементів у список - виклик функції append(), щоб додати елемент в кінець списку:

>>> planets.append('Uranus')
>>> planets
['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus', 'Uranus']
Функція append() додає елементи лише в кінець списку. Коли потрібно додати елемент у конкретну позицію, використовується функція insert().

Якщо вказати позицію 0, елемент буде додано в початок списку. Якщо позиція знаходиться за межами списку, елемент буде додано в кінець списку, як і у випадку з функцією append(), тому вам не потрібно турбуватися про те, що Python згенерує помилку:

>>> planets.insert(3, 'Neptune')
>>> planets
['Mercury', 'Jupiter', 'Earth', 'Neptune', 'Mars', 'Venus', 'Uranus']
>>> planets.insert(10, 'Unknown planet')
>>> planets
['Mercury', 'Jupiter', 'Earth', 'Neptune', 'Mars', 'Venus', 'Uranus', 'Unknown planet']

4.9. Об’єднання спиcків

Для об’єднання одного списку з іншим використовують функцію extend().
>>> planets = ['Earth', 'Mars']
>>> satellites = ['Moon', 'Deimos', 'Phobos']
>>> planets.extend(satellites)
>>> planets
['Earth', 'Mars', 'Moon', 'Deimos', 'Phobos']

Можна також використовувати операцію += для об’єднання списків:

>>> planets = ['Earth', 'Mars']
>>> satellites = ['Moon', 'Deimos', 'Phobos']
>>> planets += satellites
>>> planets
['Earth', 'Mars', 'Moon', 'Deimos', 'Phobos']

Якщо використовувати функцію append(), список satellites був би доданий як один елемент списку, замість того, щоб об’єднати його елементи зі списком planets:

>>> planets = ['Earth', 'Mars']
>>> satellites = ['Moon', 'Deimos', 'Phobos']
>>> planets.append(satellites)
>>> planets
['Earth', 'Mars', ['Moon', 'Deimos', 'Phobos']]

Це ще раз демонструє, що список може містити елементи різних типів.

4.10. Видалення елементів зі списку

Для видалення елементів зі списку користуються функцією del().
>>> planets = ['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus']
>>> del planets[-1]
>>> planets
['Mercury', 'Jupiter', 'Earth', 'Mars']
Коли видаляється заданий елемент, всі інші елементи, які йдуть слідом за ним, зміщуються вліво, щоб зайняти місце видаленого елемента, а довжина списку зменшується на одиницю.
>>> planets = ['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus']
>>> planets[3]
'Mars'
>>> del planets[3]
>>> planets
['Mercury', 'Jupiter', 'Earth', 'Venus']
>>> planets[3]
'Venus'
>>>
Зі списку можна видалити елемент не лише за індексом, а за значенням, використовуючи функцію remove().

Це той випадок, коли не відомо у якій позиції знаходиться елемент, але відоме його значення:

>>> planets = ['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus']
>>> planets.remove('Jupiter')
>>> planets
['Mercury', 'Earth', 'Mars', 'Venus']
Можна видалити елемент зі списку і, водночас, отримати його за допомогою функції pop().

Якщо викликати цю функцію і вказати деякий зсув, вона поверне елемент, що знаходиться в заданій позиції; якщо аргумент не вказано, буде використано значення -1, як у наступному прикладі:

>>> planets = ['Mercury', 'Jupiter', 'Earth', 'Mars', 'Venus']
>>> planets.pop()
'Venus'
>>> planets
['Mercury', 'Jupiter', 'Earth', 'Mars']
>>> planets.pop(1)
'Jupiter'
>>> planets
['Mercury', 'Earth', 'Mars']

4.11. Чи є елемент у списку?

В Python наявність елемента у списку перевіряється за допомогою оператора in:

>>> countries = ['England', 'France', 'Italy', 'Ukraine', 'Poland']
>>> 'Poland' in countries
True
>>> 'Hungary' in countries
False

Для перевірки на відсутність елемента у списку користуються поєднанням операторів not in:

>>> countries = ['England', 'France', 'Italy', 'Ukraine', 'Poland']
>>> 'Ukraine' not in countries # 'Ukraine' немає у списку countries? Хибність, такий елемент є у списку
False
>>> 'Portugal' not in countries # 'Portugal' немає у списку countries? Істина, такого елемента у списку немає
True

4.12. Кількість значень у списку

Щоб визначити, скільки разів якесь значення зустрічається у списку, використовується функція count().
>>> fruits = ['blueberry', 'grape', 'orange', 'plum', 'pear', 'blueberry', 'pear', 'melon', 'pear']
>>> fruits.count('grape')
1
>>> fruits.count('peach')
0
>>> fruits.count('pear')
3
>>> fruits.count('blueberry')
2

4.13. Сортування списків

Для зміни порядку розташування елементів списку за їхнім значенням у Python є дві функції:

  • sort() - сортує сам список (змінює його)

  • sorted() - повертає відсортовану копію списку (без зміни оригінального списку)

Якщо елементи списку є числами, вони за замовчуванням сортуються за зростанням. Якщо вони є рядками, то сортуються в алфавітному порядку.

Для даного списку:

>>> clothes = ['shirt', 'hat', 'jeans', 'trainers']
>>> sorted_clothes = sorted(clothes)
>>> sorted_clothes
['hat', 'jeans', 'shirt', 'trainers']
>>> clothes
['shirt', 'hat', 'jeans', 'trainers']

sorted_clothes - це копія списку clothes, її створення не змінило оригінальний список clothes. А використання функції sort() змінює сам список clothes:

>>> clothes.sort()
>>> clothes
['hat', 'jeans', 'shirt', 'trainers']

В даних прикладах усі елементи списку одного типу (рядки). Інколи можна змішувати типи - наприклад, цілі числа і числа з плаваючою точкою, оскільки в рамках сортування вони конвертуються автоматично:

>>> numbers = [5, 3, 7.0, 4]
>>> numbers.sort()
>>> numbers
[1, 2, 3, 4.0]

За замовчуванням, список сортується за зростанням, але додавши аргумент зі значенням reverse=True, список відсортується за спаданням:

>>> numbers = [5, 3, 7.0, 4]
>>> numbers.sort(reverse=True)
>>> numbers
[7.0, 5, 4, 3]

4.14. Список у зоротньому порядку

Щоб переставити елементи списку у зворотному порядку, використовується функція reverse().
>>> cars = ['bmw', 'audi', 'toyota', 'subaru']
>>> cars
['bmw', 'audi', 'toyota', 'subaru']
>>> cars.reverse()
>>> cars
['subaru', 'toyota', 'audi', 'bmw']

4.15. Створення числових списків

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

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

Для спрощення побудови числових послідовностей використовують функцію range():

>>> numbers = list(range(1,6))
>>> numbers
[1, 2, 3, 4, 5]
Функція range() дозволяє генерувати числові послідовності у заданому діапазоні.

Наприклад, для побудови списку парних чисел від 1 до 10 використовується такий код:

>>> even_numbers = list(range(2,11,2))
>>> even_numbers
[2, 4, 6, 8, 10]

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

>>> digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> min(digits)
0
>>> max(digits)
9
>>> sum(digits)
45

4.16. Кортежі

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

Для створення порожнього кортежу використовується оператор ():

>>> empty_tuple = ()
>>> empty_tuple
()

Щоб створити кортеж, що містить один елемент або більше, ставте після кожного елемента кому. Це варіант для кортежів з одним елементом:

>>> сurrency_units = 'pound',
>>> сurrency_units
('pound',)

При відображенні кортежу Python виводить на екран дужки. Якщо у кортежі більше одного елемента, ставте кому після кожного з них, крім останнього:

>>> сurrency_units = 'pound', 'dollar', 'euro'
>>> сurrency_units
('pound', 'dollar', 'euro')

Кортежі дозволяють присвоїти значення для кількох змінних за один раз:

>>> сurrency_units = 'pound', 'dollar', 'euro'
>>> p, d, e = сurrency_units
>>> p
'pound'
>>> d
'dollar'
>>> e
'euro'

Ви можете використовувати кортежі, щоб обмінюватися значеннями за допомогою одного виразу, без застосування тимчасової змінної:

>>> network_name = 'netis'
>>> password = 'pass12345'
>>> network_name, password = password, network_name
>>> network_name
'pass12345'
>>> password
'netis'

Функція перетворення tuple() створює кортежі з інших об’єктів:

>>> сurrency_units = ['pound', 'dollar', 'euro']
>>> tuple(сurrency_units)
('pound', 'dollar', 'euro')

4.17. Завдання

4.17.1. Контрольні запитання

  1. Що означають дужки []?

  2. У змінній letters міститься список ['a', 'b', 'c', 'd']. Яке значення виразу letters[int('3' * 2) / 11]?

  3. У змінній letters міститься список ['a', 'b', 'c', 'd']. Яке значення виразу letters[-1]?

  4. У змінній letters міститься список ['a', 'b', 'c', 'd']. Яке значення виразу letters[:2]?

  5. У змінній letters міститься список [2, 4, 6, 8, 10]. Як присвоїти значення 'zero' в якості третього елемента даного списку?

  6. У змінній metering міститься список [3.14, 'inch', 2.54, 'inch', True]. Яке значення виразу metering.index('inch')?

  7. Як буде виглядати список, який зберігається у змінній metering = [3.14, 'inch', 2.54, 'inch', True], після виконання команди metering.append(99)?

  8. Як буде виглядати список, який зберігається у змінній metering = [3.14, 'inch', 2.54, 'inch', True], після виконання команди metering.remove('inch')?

  9. В чому різниця між функціями append() та insert(), передбаченими для списків?

  10. Назвіть способи видалення значень із списку.

  11. Назвіть декілька спільних (відмінних) ознак для рядків і списків.

  12. Змінні, які «зберігають» список, насправді, не зберігають в собі безпосередньо сам список. Що тоді вони містять?

  13. Чим кортежі відрізняються від списків?

  14. Як створити кортеж, який міститиме єдине значення у вигляді цілого числа 24?

  15. Як перетворити список в кортеж?

4.17.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

  1. Збережіть імена кількох своїх друзів у списку з ім’ям names. Виведіть ім’я кожного друга, звернувшись до кожного елементу списку (по одному разу).

  2. Cтворіть список з типами транспортих засобів. Використайте список для виведення твердження, на зразок: «Я хотів би купити велосипед.».

  3. Створіть список years_list, що містить рік, в який ви народилися, і кожен наступний рік аж до вашого п’ятого дня народження. Наприклад, якщо ви народилися в 1995 році, список буде виглядати так: years_list = [1995, 1996, 1997, 1998, 1999, 2000]. Виведіть на екран, у якому із років, що міститься у списку years_list, вам виповнилося 3 роки? Пам’ятайте, у перший рік вам було 0 років. Додайте у кінець списку ще один рік і виведіть список на екран. У якому із років, перерахованих у списку years_list, вам було найбільше років?

  4. Створіть список things, що містить три елементи: 'wallet', 'mirror', 'umbrella'. Виведіть на екран той елемент у списку things, який має відношення до дощу, написавши його з великої літери, а потім виведіть список. Переведіть дощовий елемент списку things у верхній регістр цілком і виведіть список. Видаліть річ, яка захищає від дощу, зі списку things, а потім виведіть список на екран.

  5. Створіть список, який називається languages і містить елементи 'Georgian', 'Estonian' і 'Ukrainian'. Напишіть останній елемент списку languages з малої літери, потім переверніть його і напишіть з великої літери.

  6. Збережіть кілька понять, пов’язаних з комп’ютерами та Інтернетом, у кортежі і у списку. Назвіть відповідно їх hardware (кортеж) і software (список). Виведіть усі назви по черзі. Спробуйте замінити один з елементів у списку і у кортежі, і зробіть висновок щодо можливості зміни елементів для цих двох типів.

4.17.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

  1. Збережіть назви мов світу (Ukrainian, French, Bulgarian, Norwegian, Latvian або інші) у списку. Простежте за тим, щоб елементи у списку не зберігались в алфавітному порядку. Застосуйте функції sorted(), reverse(), sort() до списку. Виведіть список на екран до і після використання кожної із функцій.

  2. На вхід програми подається один рядок з цілими числами. Числа розділені пропусками. Необхідно вивести суму цих чисел. Наприклад, якщо був введений рядок чисел 2 -1 9 6, то результатом роботи програми буде їх сума 16.

  3. Дано список з такими елементами: cities = ['Budapest', 'Rome', 'Istanbul', 'Sydney', 'Kiev', 'Hong Kong']. Сформуйте з елементів списку повідомлення, у якому перед останнім елементом буде вставлено слово and. Наприклад, у нашому випадку, повідомлення буде таким: Budapest, Rome, Istanbul, Sydney, Kiev and Hong Kong. Програма має працювати з будь-якими списками, довжина яких є 6.

  4. Необхідно зчитати рядок з 5 цифр, розділених пропусками, і зберегти кожну цифру у список. Створіть копію списку із впорядкованими елементами у зворотному порядку. Виведіть число, яке утворюється об’єднанням елементів нового списку.

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

  6. Виконайте візуалізацію структури коду програми. Використайте у візуалізації структури елементи кортежу keywords = ('for', 'if', 'else', 'in', ':'). У процесі виведення структури коду на екран, враховуйте відступи рядків від лівого краю, у розрахунку один відступ - 4 пропуски. Вигляд структури коду має бути таким:

for each token in the postfix expression :
    if the token is a number :
        print('Convert it to an integer and add it to the end of values')
    else
        print('Append the result to the end of values')

5. Словники і множини

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

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

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

Словники можна змінювати. Це означає, що можна додати, видалити і змінити їх елементи, які мають вигляд ключ - значення. Якщо ключ визначений, змінити його не можна.

В інших мовах програмування словники можуть називатися асоціативними масивами, хешами або хеш-таблицею. В Python словник також називається dict.

5.1. Створення словника

Найпростішим словником є порожній словник, що не містить ні ключів, ні значень, і створюється за допомогою оператора {}:

>>> empty_dict = {}
>>> empty_dict
{}

Щоб створити словник зі значеннями, потрібно помістити у фігурні дужки {} пари ключ: значення, розділені комами:

>>> myDog = {'name': 'Australian Shepherd', 'training': '5', 'intelligence': 5, 'type': 'Companion'}
>>> myDog
{'intelligence': 5, 'training': '5', 'name': 'Australian Shepherd', 'type': 'Companion'}

5.2. Перетворення типів: функція dict()

Функція dict() використовується для перетворення послідовності з двох значень у словник.

Наприклад, такими послідовностями ключ - значення є послідовності виду:

  • Кремній: 28, Карбон: 12

  • Перемог: 12, Поразок: 7, Нічиїх: 3

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

>>> couple = [ ['a', 'b'], ['c', 'd'], ['e', 'f'] ]
>>> dict(couple)
{'e': 'f', 'c': 'd', 'a': 'b'}
Порядок ключів у словнику може бути довільним, він залежить від того, як ви додаєте елементи.

5.3. Додавання і зміна елементів словника

Додати елемент в словник досить легко. Потрібно просто звернутися до елементу по його ключу і присвоїти йому значення.

Якщо ключ вже існує у словнику, поточне значення буде замінено новим. Якщо ключ новий, то він і вказане значення будуть додані в словник.

Створимо словник, що містить імена розробників популярних мов програмування, використовуючи назви мов в якості ключів, а імена та прізвища розробників - як значення:

>>> languages_programmers = {
... 'JavaSript': 'Brendan Eich',
... 'Python': 'Guido van Rossum',
... 'Ruby': 'Yukihiro Matsumoto',
... 'PHP': 'Rasmus Lerdorf',
... 'Scala': 'Martin Odersky'
... }
>>> languages_programmers
{'Python': 'Guido van Rossum', 'Scala': 'Martin Odersky', 'Ruby': 'Yukihiro Matsumoto', 'PHP': 'Rasmus Lerdorf', 'JavaSript': 'Brendan Eich'}

Додамо у словник інформацію про ще одного видатного вченого-інформатика (розробника мови C):

>>> languages_programmers['C'] = 'Dennis MacAlistair Ritchie'
>>> languages_programmers
{'Scala': 'Martin Odersky', 'JavaSript': 'Brendan Eich', 'Python': 'Guido van Rossum', 'PHP': 'Rasmus Lerdorf', 'Ruby': 'Yukihiro Matsumoto', 'C': 'Dennis MacAlistair Ritchie'}

Ім’я розробника доданого останнього значення занадто довге (складається з трьох слів). Виправимо це:

>>> languages_programmers['C'] = 'Dennis Ritchie'
>>> languages_programmers
{'Scala': 'Martin Odersky', 'JavaSript': 'Brendan Eich', 'Python': 'Guido van Rossum', 'PHP': 'Rasmus Lerdorf', 'Ruby': 'Yukihiro Matsumoto', 'C': 'Dennis Ritchie'}

Використовуючи один і той же ключ ('C'), ми замінили початкове значення 'Dennis MacAlistair Ritchie' на 'Dennis Ritchie'.

Ключі у словнику повинні бути унікальними. Перед створенням словника, поміркуйте, які значення можуть бути ключами.

Якщо ви застосовуєте ключ більш ніж один раз, зараховується лише останнє значення:

>>> languages_programmers = {
... 'JavaSript': 'Brendan Eich',
... 'Python': 'Guido van Rossum',
... 'C': 'Dennis MacAlistair Ritchie',
... 'Ruby': 'Yukihiro Matsumoto',
... 'PHP': 'Rasmus Lerdorf',
... 'Scala': 'Martin Odersky',
... 'C': 'Dennis Ritchie'
... }
>>> languages_programmers
{'PHP': 'Rasmus Lerdorf', 'Scala': 'Martin Odersky', 'JavaSript': 'Brendan Eich', 'Python': 'Guido van Rossum', 'C': 'Dennis Ritchie', 'Ruby': 'Yukihiro Matsumoto'}

5.4. Об’єднання словників

Використовуючи функцію update(), можна скопіювати ключі і значення з одного словника в інший.

Визначимо словник languages_programmers, що містить імена відомих розробників мов програмування:

>>> languages_programmers = {
... 'JavaSript': 'Brendan Eich',
... 'Python': 'Guido van Rossum',
... 'Ruby': 'Yukihiro Matsumoto',
... 'PHP': 'Rasmus Lerdorf',
... 'Scala': 'Martin Odersky',
... 'C': 'Dennis Ritchie'
... }
>>> languages_programmers
{'Scala': 'Martin Odersky', 'JavaSript': 'Brendan Eich', 'Python': 'Guido van Rossum', 'PHP': 'Rasmus Lerdorf', 'Ruby': 'Yukihiro Matsumoto', 'C': 'Dennis Ritchie'}

Крім того, у нас є інший словник others, що містить імена інших розробників:

>>> others = {'C++': 'Bjarne Stroustrup', 'Swift': 'Chris Lattner'}

Додамо розробників словника others у основний словник languages_programmers:

>>> languages_programmers.update(others)
>>> languages_programmers
{'Scala': 'Martin Odersky', 'JavaSript': 'Brendan Eich', 'Python': 'Guido van Rossum', 'PHP': 'Rasmus Lerdorf', 'Ruby': 'Yukihiro Matsumoto', 'Swift': 'Chris Lattner', 'C': 'Dennis Ritchie', 'C++': 'Bjarne Stroustrup'}

Що станеться, якщо в другому словнику будуть знаходитися такі ж ключі, що і в першому? Переможе значення з другого словника:

>>> first = {'a': 10, 'b': 20}
>>> second = {'b': 'other'}
>>> first.update(second)
>>> first
{'a': 10, 'b': 'other'}

5.5. Видалення елементів із словника

Скасуємо додавання у словник розробників останніх двох елементів:

>>> del  languages_programmers['Swift']
>>> languages_programmers
{'Scala': 'Martin Odersky', 'JavaSript': 'Brendan Eich', 'Python': 'Guido van Rossum', 'PHP': 'Rasmus Lerdorf', 'Ruby': 'Yukihiro Matsumoto', 'C': 'Dennis Ritchie', 'C++': 'Bjarne Stroustrup'}
>>> del  languages_programmers['C++']
>>> languages_programmers
{'Scala': 'Martin Odersky', 'JavaSript': 'Brendan Eich', 'Python': 'Guido van Rossum', 'PHP': 'Rasmus Lerdorf', 'Ruby': 'Yukihiro Matsumoto', 'C': 'Dennis Ritchie'}
Щоб видалити усі ключі і значення зі словника, слід скористатися функцією clear() або просто присвоїти порожній словник заданому імені.
>>> languages_programmers.clear()
>>> languages_programmers
{}
>>> languages_programmers = {}
>>> languages_programmers
{}

5.6. Чи є ключ у словнику?

Якщо ви хочете дізнатися, чи міститься в словнику якийсь ключ, використовуйте ключевое слово in. Знову визначимо словник languages_programmers, але на цей раз з меншою кількістю елементів:

>>> languages_programmers = {'JavaSript': 'Brendan Eich', 'Python': 'Guido van Rossum', 'Ruby': 'Yukihiro Matsumoto'}
>>> 'Ruby' in languages_programmers
True
>>> 'Scala' in languages_programmers
False

5.7. Значення у словнику

Для отримання певне значення зі словника, ви вказуєте словник і ключ для цього значення:

>>> languages_programmers['Ruby']
'Yukihiro Matsumoto'

Якщо ключа у словнику немає, буде згенеровано помилку:

>>> languages_programmers['PHP']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'PHP'
Щоб уникати помилок при роботі із словником, спочатку треба перевірити чи є ключ у словнику або скористатися функцією get().

Ви вказуєте словник, ключ і опціональне значення. Якщо ключ існує, ви отримаєте пов’язане з ним значення:

>>> languages_programmers.get('Ruby')
'Yukihiro Matsumoto'

Якщо такого ключа немає, отримують опціональне значення, якщо воно було визначено:

>>> languages_programmers.get('PHP', 'PHP not in dictionary.')
'PHP not in dictionary.'

У іншому випадку буде повернуто об’єкт None (інтерактивний інтерпретатор нічого не виведе):

>>> languages_programmers.get('PHP')
>>>

5.8. Ключі, значення, пари "ключ - значення" зі словника

Щоб отримати всі ключі словника можна використати функцію keys().

Для наступних прикладів використаємо інший словник:

>>> light_signals = {'green': 'go', 'yellow': 'get ready', 'red': 'stop'}
>>> light_signals.keys()
dict_keys(['yellow', 'red', 'green'])
В Python 3 функція keys() повертає не список ключів, а dict_keys() - ітеративне представлення ключів.

Ітеративне представлення ключів зручно для великих словників, окільки не вимагає часу і пам’яті для створення і збереження списку з цими ключами. Але найчастіше нам потрібен саме список. Тому в Python 3 треба викликати функцію list(), щоб перетворити dict_keys у список.

Отримання усіх ключів словника у вигляді списку з використанням функції list():

>>> list(light_signals.keys())
['yellow', 'red', 'green']

В Python 3 функція list() використовується для перетворення результатів роботи функцій values() і items() у звичайні списки.

Щоб отримати всі значення словника, необхідно використовувати функцію values().
>>> list(light_signals.values())
['get ready', 'stop', 'go']
Щоб отримати усі пари «ключ: значення» зі словника, необхідно використати функцію items().
>>> list(light_signals.items())
[('yellow', 'get ready'), ('red', 'stop'), ('green', 'go')]

Як видно із результату, кожна пара «ключ: значення» повернулася у вигляді кортежу.

5.9. Множини

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

Щоб створити множину, слід використовувати функцію set() або розмістити у фігурних дужках одне або кілька значень, розділених комами.

Для прикладу:

>>> empty_set = set()
>>> empty_set
set()
>>> numbers = {1, 3, 5, 7, 9}
>>> numbers
{9, 1, 3, 5, 7}
Як і у випадку зі словником, порядок ключів у множині не має значення.

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

>>> set('office')
{'e', 'c', 'o', 'f', 'i'}
Множина містить тільки одне входження літери f, незважаючи на те, що у слові office є два входження цієї літери.

5.10. Складені структури даних

Різні типи даних (булеві значення, числа і рядки, списки, кортежі, множини і словники) можна об’єднувани у власні структури, більші і складніші. Для прикладу, візьмемо три різні списки:

>>> dairy_products = ['butter', 'cheese', 'yoghurt']
>>> vegetables = ['potatoes', 'broccoli', 'carrot', 'cucumber', 'onion']
>>> light_snacks = ['biscuits', 'chocolate', 'nuts']

Ми можемо створити кортеж, який містить в якості елементів кожен з цих списків:

>>> tuple_of_lists = dairy_products, vegetables, light_snacks
>>> tuple_of_lists
(['butter', 'cheese', 'yoghurt'], ['potatoes', 'broccoli', 'carrot', 'cucumber', 'onion'], ['biscuits', 'chocolate', 'nuts'])

Можемо також створити список, який містить три списки:

>>> list_of_lists = [dairy_products, vegetables, light_snacks]
>>> list_of_lists
[['butter', 'cheese', 'yoghurt'], ['potatoes', 'broccoli', 'carrot', 'cucumber', 'onion'], ['biscuits', 'chocolate', 'nuts']]

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

>>> dict_of_lists = {'Dairy': dairy_products, 'Vegetables': vegetables, 'Snacks': light_snacks}
>>> dict_of_lists
{'Snacks': ['biscuits', 'chocolate', 'nuts'], 'Vegetables': ['potatoes', 'broccoli', 'carrot', 'cucumber', 'onion'], 'Dairy': ['butter', 'cheese', 'yoghurt']}
Ключі словника повинні бути незмінними, тому список, словник чи множина не можуть бути ключем для іншого словника. Але кортеж може бути ключем.

Наприклад, ви можете створити словник понять різних видів спорту:

>>> sports_equipment = {
... ('1500 metres', 'high jump', 'shot put'): 'Athletics',
... ('goalkeeper', 'foul', 'corner'): 'Football',
... ('handlebars', 'wheels', 'bicycle pump'): 'Cycling'
... }
>>> sports_equipment
{('1500 metres', 'high jump', 'shot put'): 'Athletics', ('handlebars', 'wheels', 'bicycle pump'): 'Cycling', ('goalkeeper', 'foul', 'corner'): 'Football'}

5.11. Завдання

5.11.1. Контрольні запитання

  1. Як виглядає код для порожнього словника?

  2. Як виглядає елемент словника з ключем key і значенням 81?

  3. Опишіть основні відмінності між словником і списком.

  4. У змінній result зберігається словник. Яка різниця між такими виразами: 'battery' in result i 'battery' in result.keys()?

  5. У змінній result зберігається словник. Яка різниця між такими виразами: 'battery' in result i 'battery' in result.values()?

  6. Що буде відображено на місці ... при спробі отримання доступу до елементів, використовуючи такий код:

>>> room = {'bookcase': 1, 'armchair': 3, 'table': 1, 'clock': True}
>>> room['armchair']
...
>>> room['mirror']
...
>>> room['clock']
...
>>> room['table']
...
>>> room.get('blinds', 'Blinds are missing.')
...

5.11.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

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

  2. Створіть словник для зберігання цифр. Візьміть п’ять імен (назви цифр) і використайте їх як ключі словника. Для кожного ключа надайте значення відповідної цифри. Виведіть по черзі назву цифри і її значення для усіх елементів словника.

  3. У французько-англійському словнику f2e містяться такі слова: blanc / white, rose / pink, violet / purple і argent / silver. Опишіть цей словник. Створіть і виведіть на екран множину французьких слів з ключів словника.

  4. Створіть множину із списку: ['boat', 'bus', 'plane', 'train'].

  5. Створіть множину із кортежу: ('cyclist', 'driver', 'pedestrian').

  6. Створіть множину із словника: {'breakfast': 'coffee', 'lunch': 'milk', 'dinner': 'tea'}.

5.11.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

  1. Словники Python можуть використовуватися для моделювання «справжнього» словника (назвемо його глосарієм). Оберіть кілька термінів з програмування (або із іншої області), які ви знаєте на цей момент. Використайте ці слова як ключі глосарію, а їх визначення - як значення. Виведіть кожне слово і його визначення у спеціально відформатованому вигляді. Наприклад, ви можете вивести слово, потім двокрапка і визначення; або ж слово в одному рядку, а його визначення - з відступом в наступному рядку. Використовуйте символ нового рядка (\n) для вставки порожніх рядків між парами слово: визначення і символ табуляції для встановлення відступів (\t) у вихідних даних.

  2. Cтворіть словник з трьома річками і регіонами, територією яких вони протікають. Одна з можливих пар «ключ: значення» - 'Amazon': 'South America'. Додайте ще дві пари «річка: регіон» у словник. Виведіть повідомлення із назвами річки і регіону - наприклад, «The Amazon runs through South America.» для усіх елементів словника, враховуючи те, що у повідомлення у відповідні місця підставляються назви річок і територій.

  3. Створіть словник з чотирма назвами мов програмування (ключі) та іменами розробників цих мов (значення). Виведіть по черзі для усіх елементів словника повідомлення типу My favorite programming language is Python. It was created by Guido van Rossum.. Видаліть, на ваш вибір, одну пару «мова: розробник» із словника. Виведіть словник на екран.

  4. Створіть англо-німецький словник, який називається e2g, і виведіть його на екран. Слова для словника: stork / storch, hawk / falke, woodpecker / specht і owl / eule. Виведіть німецький варіант слова owl. Додайте у словник, на ваш вибір, ще два слова та їхній переклад. Виведіть окремо: словник; ключі і значення словника у вигляді списків.

  5. Створіть кілька словників, імена яких - це клички домашніх тварин. У кожному словнику збережіть інформацію про вид домашнього улюбленця та ім’я власника. Збережіть словники в списку з ім’ям pets. Виведіть кілька повідомлень типу Alex is the owner of a pet - a dog..

  6. Виведіть представлення букви, яку вводить користувач, у символах азбуки Морзе. Фрагмент словника має такий вигляд: morse = {'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.', 'H': '…​.', 'I': '..', і т. д.}. Передбачте у програмі обробку малих і великих букв.

  7. Створіть багаторівневий словник subjects навчальних предметів. Використайте наступні рядки для ключів верхнього рівня: 'science', 'humanities' і 'public'. Зробіть так, щоб ключ 'science' був ім’ям іншого словника, який має ключі 'physics', 'computer science' і 'biology'. Зробіть так, щоб ключ 'physics' посилався на список рядків зі значеннями 'nuclear physics', 'optics' і 'thermodynamics'. Решта ключів повинні посилатися на порожні словники. Виведіть на екран ключі subjects['science'] і значення subjects['science']['physics'].

  8. Створіть словник з ім’ям cities. Використайте назви трьох міст в якості ключів словника. Створіть словник з інформацією про кожне місто: включіть в нього країну, в якій розташоване місто, приблизну чисельність населення і один цікавий факт про місто. Ключі словника кожного міста повинні називатися country, population і fact. Виведіть назву кожного міста і всю збережену інформацію про нього.

  9. Із словника teams необхідно вивести на екран статистику кількох команд Національної баскетбольної асоціації NBA. Створіть словник teams, дотримуючись наступних правил. Назви команд - це ключі словника. Значення у словнику - це список, на зразок: [Всього ігор, Перемог, Нічиїх, Поразок, Всього очок]. Значення списку - це цілі числа, які обираються довільно. При виведенні даних, прослідкуйте, щоб формат виведення був таким: NEW YORK KNICKS 22 7 6 9 45. Інформація про кожну команду має міститися в окремому рядку. Зверніть увагу на те, що дані у словнику є невпорядкованими.

  10. Ви створюєте пригодницьку гру і використовуєте для зберігання предметів гравця словник, у якому ключі - це назви предметів, значення - кількість одиниць кожної із речей. Наприклад, словник може виглядати так: things = {'key': 3, 'mace': 1, 'gold coin': 24, 'lantern': 1, 'stone': 10}. Виведіть повідомлення про усі речі гравця у такому вигляді:

Equipment:
3 key
1 mace
24 stone
1 lantern
10 gold coin
Total number of things: 39

6. Структури коду

Програма - це всього лише серія інструкцій. Виходячи з того, як оцінюються вирази, програма може вирішити пропустити інструкції, повторити їх або вибрати одну з декількох для запуску. Тобто, у програмі часто доводиться перевіряти умови і приймати рішення в залежності від цих умов.

Розгляньте блок-схему прийняття рішень, яка визначає порядок дій, які повинні виконуватися в залежності від того, чи йде дощ:

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

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

6.1. Створення і перевірка умов

Для створення і перевірки умов використовують булеві значення, оператори порівняння і булеві оператори.

6.1.1. Булеві значення

В той час, як цілі, дійсні і рядкові типи даних можуть мати необмежену кількість значень, булевий (логічний) тип даних може прйимати лише два значення: True (істина) і False (хибність).

Булевий тип даних отримав свою назву на честь англійського математика, засновника математичної логіки Джорджа Буля.
>>> one = True
>>> two = false
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'false' is not defined
>>> two = False
>>> one
True
При використанні у коді Python, булеві значення True і False записуються без лапок і їх назви починаються з великих літер T і F, тоді як інша частина слова пишеться маленькими буквами.

6.1.2. Оператори порівняння

Оператори порівняння порівнюють два значення між собою і повертають результат у вигляді булевого значення: істину або хибність.

Таблиця "Оператори порівняння"
Оператор Назва

==

Дорівнює

!=

Не дорівнює

<

Менше ніж

>

Більше ніж

<=

Менше або дорівнює

>=

Більше або дорівнює

Для прикладу, присвоємо змінній a цілочисельне значення 10:

>>> a = 10
>>> a == 5
False
>>> a == 10
True
>>> 4 < a
True
>>> a > 12
False
>>> 'breeze' == 'breeze'
True
>>> 'breeze' == 'Breeze'
False
>>> 'fine' != 'rain'
True
>>> 16 == 16.0
True
>>> 16 == '16'
False
>>> 45 >= 11
True
Будьте уважними, для перевірки на рівність використовуються два знака «дорівнює» (==); пам’ятайте, що один знак «дорівнює» (=) застосовується для присвоювання змінній значення.

6.1.3. Булеві оператори

Якщо вам потрібно виконати кілька порівнянь одночасно, використовуйте булеві (логічні) оператори and, or і not, щоб визначити підсумковий результат. Булеві оператори мають більш низький пріоритет, ніж фрагменти коду, які вони порівнюють. Це означає, що спочатку вираховується результат фрагментів, а потім вони порівнюються.

Булеві оператори and і or завжди працюють з двома булевими значеннями (або виразами), тому їх називають бінарними.

Для представлення усім можливих результатів роботи булевих операторів and і or використовують таблиці істинності.

Таблиця істинності для оператора and
Вираз Результат

True and True

True

True and False

False

False and True

False

False and False

False

Таблиця істинності для оператора or
Вираз Результат

True or True

True

True or False

True

False or True

True

False or False

False

Булевий оператор not змінює булеве значення на протилежне.

Булевий оператори not завжди діє на одне булеве значення (або вираз), тому його називають унарним.
Таблиця істинності для оператора not
Вираз Результат

not True

False

not False

True

Щоб побачити як працюють булеві оператори and, or і not, виконайте в інтерпретаторі наступні приклади:

>>> True and True
True
>>> True and False
False
>>> True or False
True
>>> not True
False
>>> not False
True

6.1.4. Поєднання булевих значень, операторів порівнювання, булевих операторів

Виконайте в інтерпретаторі Python наступні приклади:

>>> (7 < 10) and (6 < 9)
True
>>> (3 < 5) and (16 < 8)
False
>>> (3 == 6) or (32 == 32)
True
>>> 2 + 2 == 4 and not 2 + 2 == 5 and 2 * 2 == 2 + 2
True

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

Як і для математичних операторів, для булевих операторів визначений порядок їхнього виконання. Після того, як будуть виконані всі математичні оператори і оператори порівнювання, першими виконуються оператори not, потім - оператори and і тільки після цього - оператори or.

6.2. Вказівка розгалуження

До цього моменту, в багатьох випадках для ознайомлення з типами даних використовувався інтерактивний інтерпетатор. У наступних прикладах увага буде приділятися структурі коду Python. Тому приклади коду необхідно записувати в окремих файлах, створених у вибраному середовищі програмування.

Будь-який булевий вираз може розглядатися як умова, обчислення якої завжди дає булеве значення True (істина) або False (хибність). В залежності, яке значення отримує умова, програма може виконувати або пропускати фрагменти коду - блоки коду, тобто має місце розгалуження.

Ознакою блоку коду є збільшення відступу від лівого краю. Блоки можуть містити інші блоки. Ознакою кінця блоку слугує зменшення відступу до нуля або до величини відступу зовнішнього блоку.

Для більшого розуміння про відступи і блоки коду, розглянемо наступну програму (створіть новий файл у вибраному вами середовищі програмування і збережіть файл з певним ім’ям):

name = 'Mary'
password = 'hurricane'
if name == 'Mary':
....print('Hello, Mary.') (1)
....if password == 'swordfish':
........print('Access granted.') (2)
....else:
........print('Wrong password.') (3)
else:
....print('User not registered.') (4)
Відступи явно позначені крапками для наочності. В реальних умовах відступи створюють за допомогою клавіші Пропуск.
  1. Перший блок коду: починається рядком print('Hello, Mary.) і містить усі наступні рядки коду до другого оператора else (не включаючи його).

  2. Другий блок коду: знаходиться у першому блоці коду, містить лише один рядок коду print('Access granted.') і відноситься до другого оператора if.

  3. Третій блок коду: складається тільки із одного рядка print('Wrong password.') і відноситься до першого оператора else.

  4. Четвертий блок коду: складається із єдиного рядка print('User not registered.') і відноситься до другого оператора else.

6.2.1. Що істина, а що хибність?

Як визначається істина або хибність, якщо елемент в умові, який ми перевіряємо, не є булевим? Наприклад, до False (хибність) прирівнюються наступні значення:

  • булева змінна False

  • значення None

  • ціле число 0

  • число з плаваючою точкою 0.0

  • порожній рядок ''

  • порожній список []

  • порожній кортеж ()

  • порожній словник {}

  • порожня множина set()

Всі інші значення прирівнюються до True (істина).

6.2.2. Команда if

Завдяки команді if перевіряється поточний стан програми та вибираються подальші дії в залежності від результатів перевірки. Синтаксис команди if такий:

if умова :
	блок коду
Така форма розгалуження у програмуванні називається неповною.

Якщо результат обчислення умови є істинним (True), блок коду виконується. Коли ж результат обчислення умови є хибним (False), то блок коду не буде виконуватися.

Звичайною мовою роботу команди if можна описати таким чином: «Якщо умова істинна, то виконати даний блок коду».

В Python інструкція if включає такі елементи:

  • ключове слово if

  • умова (вираз, обчислення якого дає одне з двох значень: True або False)

  • двокрапка

  • блок коду з відступом, який починається в наступному рядку

Припустимо, у нас є код, що перевіряє співпадіння з певним ім’ям, наприклад, з ім’ям Mary:

if name == 'Mary':
    print('Hello, Mary.')

Якщо вважати, що змінній name попередньо було присвоєне значення Mary, то після виконання цього фрагмента коду, отримаємо на екрані повідомлення:

print('Hello, Mary.')

У протилежному випадку, якщо змінна name матиме інше значення, блок коду команди if, що складається з однієї інструкції print('Hello, Mary.'), не буде виконаний і жодного повідомлення на екрані не з’явиться.

Блок-схема для даного фрагмента коду буде такою:

Блок-схема інструкції if
Блок-схема неповної форми розгалуження: інструкція if

6.2.3. Команда else

За блоком коду команди if може слідувати необов’язкова команда else вже із власним блоком коду, який виконується лише в тому випадку, якщо умова if є хибною. Синтаксис цієї конструкції такий:

if умова :
	блок коду, коли умова істинна
else:
	блок коду, коли умова хибна
Така форма розгалуження у програмуванні називається повною.

Звичайною мовою конструкція if/else може бути прочитана так: «Якщо умова істинна, то виконати даний блок коду. В протилежному випадку виконати наступний блок коду».

В Python команда else не має умови і завжди складається з таких елементів:

  • ключове слово else

  • двокрапка

  • блок коду з відступом, що починається на наступному рядку

Повертаючись до прикладу з ім’ям Mary, розглянемо код, що містить інструкцію else, яка виводить інше повідомлення, якщо змінній name було попередньо надано значення не Mary, а, наприклад, Jack:

if name == 'Mary':
    print('Hello, Mary.')
else:
	print('Hello, stranger.')

Результатом виконання у даному випадку буде повідомлення із блоку коду команди else, що складається з однієї інструкції print('Hello, stranger.'):

Hello, stranger.

Блок-схема для цього випадку:

Блок-схема конструкції if/else
Блок-схема повної форми розгалуження: конструкція if/else

6.2.4. Команда elif

Для перевірки більше однієї умови використовують команду elif (скорочення від else if). Записується ця команда тільки після інструкцій if або після іншої команди elif. Дана команда дозволяє вказувати додаткові умови для перевірки. Синтаксис використання інструкції elif такий:

if умова1 :
	блок коду, коли умова1 істинна
elif умова2:
	блок коду, коли умова2 істинна
elif умова3:
	блок коду, коли умова3 істинна
і т.д.

В Python команда elif завжди складається з таких елементів:

  • ключове слово elif

  • умова (вираз, обчислення якого дає одне з двох значень: True або False)

  • двокрапка

  • блок коду з відступом, що починається на наступному рядку

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

6.2.5. Конструкція if/elif/else

При необхідності услід за останньою інcтрукцією elif можна помістити команду else. В цьому випадку гарантується, що буде виконаний хоча б один (і тільки один) блок коду:

if умова1 :
	блок коду, коли умова1 істинна
elif умова2:
	блок коду, коли умова2 істинна
elif умова3:
	блок коду, коли умова3 істинна
else:
	блок коду, коли усі умови хибні
Якщо умови у всіх інcтрукціях if і elif будуть хибними, то виконається блок коду інструкції else.

Звичайною мовою конструкція if/elif/else може бути прочитана так: «Якщо перша умова істинна, то виконати даний блок коду. Якщо друга умова істинна, виконати даний блок коду. В протилежному випадку виконати даний блок коду».

Використаємо інcтрукції if, elif і else у нашому прикладі з перевіркою імені:

if name == 'Alice':
    print('Hi, Alice.')
elif age < 12:
    print('You are not Alice, kiddo.')
else:
    print('You are neither Alice nor a little kid.')

Якщо вхідні дані вважати такими: name = 'Alex', age = 36, то результатом виконання буде повідомлення:

You are neither Alice nor a little kid.

Блок-схема для даного прикладу:

Блок-схема конструкції if/elif/else
Блок-схема: конструкція if/elif/else

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

furry = True
small = True
if furry:
    if small:
        print("It's a cat.")
    else:
        print("It's a bear!")
else:
    if small:
        print("It's a lizard!")
    else:
        print("It's a human. Or a hairless bear.")

Результатом виконання цього фрагмента програми буде повідомлення:

It's a cat.

6.3. Вказівка повторення

Перевірки за допомогою if, elif і else виконуються послідовно. Іноді потрібно виконати якісь операції більше ніж один раз. Для цього потрібен цикл.

6.3.1. Команда while

Одним із варіантів циклів у Python є while. Синтаксис використання циклу while такий:

while умова :
	блок коду, коли умова істинна

В Python команда while завжди складається з таких елементів:

  • ключове слово while

  • умова (вираз, обчислення якого дає одне з двох значень: True або False)

  • двокрапка

  • блок коду з відступом, що починається на наступному рядку

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

У циклі while умова завжди перевіряється на початку кожної ітерації - кожен раз, коли виконується цикл. Ітерація - один крок виконання циклу.

Спробуйте запустити наступний приклад коду:

count = 1
while count <= 5:
    print(count)
    count += 1

Це простий цикл виводить на екран значення від 1 до 5:

1
2
3
4
5

Як працює цикл while у цьому прикладі?

  1. Спочатку ми присвоюємо значення 1 змінній count.

  2. Цикл while порівнює значення змінної count (зараз воно дорівнює 1) з числом 5 і продовжує роботу, якщо значення count менше або дорівнює 5 (умова циклу є істинною) або у протилежному випадку цикл завершується (умова циклу є хибною).

  3. Всередині циклу (ця частина називається тілом циклу) виводиться значення змінної count.

  4. Потім збільшується значення змінної count на 1 за допомогою виразу count += 1.

  5. Python повертається до початку циклу і знову порівнює значення змінної count з числом 5.

  6. Значення змінної count тепер дорівнює 2, тому тіло циклу while виконується знову і змінна count збільшується до 3.

  7. Виконання продовжується до тих пір, поки значення змінної count не буде збільшено з 5 до 6 в тілі циклу.

  8. Під час чергового повернення на початок циклу перевірка count <= 5 поверне значення False і цикл while закінчується.

  9. Python перейде до виконання наступних рядків коду.

Блок-схема використання циклу while у даному прикладі така (вважати, що перед виконанням циклу було виконано присвоєння count = 1):

Блок-схема циклу *while*
Блок-схема: цикл while

6.3.2. Переривання циклу, break

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

Розглянемо програму, у якій користувачем вводиться рядок з клавітури за допомогою функції input(), а потім цей рядок виводиться на екран із представленням першої літери у верхньому регістрі. Цикл переривається, коли буде введено рядок, що містить тільки букву «q».

Введіть наступний код у середовище програмування, збережіть під певним ім’ям і виконайте:

stuff = ''
while True:
	print("String to capitalize [type q to quit]: ")
    stuff = input()
    if stuff == "q":
        break
    print(stuff.capitalize())
print('Thank you!')
Рядок while True: створює нескінченний цикл - умова такого циклу завжди істинна (True). Програма завжди буде виконувати команди циклу і вийде з нього тільки у тому випадку, якщо виконається інструкція break. Нескінченний цикл, вийти з якого неможливо, - розповсюджена помилка.

Результат виконання і блок-схема, для випадку використання команди break, будуть такими:

String to capitalize [type q to quit]:
test
Test
String to capitalize [type q to quit]:
hey, it works!
Hey, it works!
String to capitalize [type q to quit]:
q
Thank you!
Блок-схема з нескінченним циклом *while* і командою *break* для виходу з нього
Блок-схема: нескінченний цикл while і команда break для виходу з нього
Перекреслений шлях виконання програми на блок-схемі логічно ніколи не може бути реалізований, оскільки умова циклу завжди істинна.

6.3.3. Нескінченний цикл і вихід з нього

Щоб перевірити на практиці, як працює нескінченний цикл, запишіть і збережіть у файлі з певним ім’ям код, поданий нижче:

Якщо ваша програма потрапила в нескінченний цикл, натисніть комбінацію клавіш Ctrl+C, в результаті чого програма припинить своє виконання.
while True:
    print('Hello, world!')

Дана програма в процесі виконання без зупинки буде повторювати виведення повідомлення Hello, world!, оскільки умова циклу while завжди істинна (True).

Щоб попередити зациклювання програми, проаналізуйте, як обробляється значення, що повинно привести до виходу з циклу. Перевірте, щоб виконання хоча б однієї частини програми могло б привести до того, щоб умова циклу стала дорівнювати False або була б виконана команда break. Наприклад, якщо у коді:

count = 1
while count <= 5:
    print(count)
    count += 1

пропустити рядок count += 1

count = 1
while count <= 5:
    print(count)

то цикл стане нескінченним:

1
1
1
...

6.3.4. Продовження циклу, continue

Іноді потрібно не переривати весь цикл, а лише пропустити, по певній причині, одну ітерацію. Для цих речей використовується оператор continue.

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

Напишемо і збережемо у файлі з певною назвою програму, яка запитує ім’я і пароль для входу:

name = ''
while True:
    print('Who are you?')
    name = input()
    if name != 'Joe':
        continue
    print('Hello, Joe. What is the password? (It is a fish.)')
    password = input()
    if password == 'swordfish':
        break
print('Access granted.')

Якщо користувач вводить будь-яке ім’я, окрім Joe, інструкція continue вказує програмі перейти на початок циклу. Після повторної перевірки умови програма завжди входить в тіло циклу, оскільки умова завжди істинна (True).

У випадку проходження існтрукції if, запитується пароль і, якщо користувач, вводить пароль swordfish, то виконується інcтрукція break, програма перериває цикл і виводить на екран повідомлення Access granted.. В протилежному випадку управління виконанням програми передається в кінець циклу while і відразу повертається на його початок.

Результи виконання програми для різних значень, що вводив користувач, і відповідна блок-схема:

Who are you?
Alice
Who are you?
Joe
Hello, Joe. What is the password? (It is a fish.)
Mary
Who are you?
Joe
Hello, Joe. What is the password? (It is a fish.)
swordfish
Access granted.
Блок-схема з нескінченним циклом *while*, командою *break* для виходу з нього і командою *continue* для продовження циклу
Блок-схема: нескінченний цикл while, команда break для виходу з нього, команда continue для продовження циклу

Програма не запитає пароль до тих пір, поки користувач не підтвердить, що його ім’я Joe, а після введення правильного пароля програма завершить своє виконання.

6.3.5. Цикл for

Цикл while продовжує виконуватися доти, доки умова залишається істинною. Якщо необхідно виконати блок коду лише визначену (відому) кількість разів, то використовують цикл for і функцію range(). Синтаксис використання цього циклу і цієї функції записується так:

for змінна in range() :
	блок коду

В Python команда for завжди складається з таких елементів:

  • ключове слово for

  • ім’я змінної

  • ключове слово in

  • виклик функції range(), в яку можна передати до трьох цілих чисел, розділених комами

  • двокрапка

  • блок коду з відступом, що починається на наступному рядку

Щоб перевірити на практиці, як працює цикл for, створіть нову програму у файлі та збережіть з певним ім’ям:

print('My name is')
for i in range(5):
    print('Jimmy Five Times (' + str(i) + ')')

Блок коду циклу for виконується 5 разів. На першій ітерації (при першому виконанні) значення змінної i встановлюється рівним 0. Виклик функцій print() в тілі циклу виводить повідомлення Jimmy Five Times (0).

Коли цикл закінчує ітерацію, виконавши увесь блок коду, управління передається на початок циклу, де інструкція for збільшує значення змінної i на 1.

Саме тому, виклик функції range(5) забезпечує п’ятикратне виконання блоку коду циклу, встановлюючи для i послідовно значення 0, 1, 2, 3 і 4.

Ціле значення 5, вказане у дужках функції range() в цей ряд не входить.

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

My name is
Jimmy Five Times (0)
Jimmy Five Times (1)
Jimmy Five Times (2)
Jimmy Five Times (3)
Jimmy Five Times (4)

Блок-схема роботи даної програми така:

Блок-схема з циклом *for*
Блок-схема: цикл for
Команди break і continue використовуються у циклі for по аналогії, як і у випадку з циклом while.

Окрім того, все те, що робить цикл for, можна зробити і за допомогою циклу while. Перепишіть попередню програму, але вже з використанням циклу while і переконайтеся у таких самих результатах, як і у випадку використання циклу for:

print('My name is')
i = 0
while i < 5:
    print('Jimmy Five Times (' + str(i) + ')')
    i = i + 1

6.3.6. Функція range()

У функцію range() можна передавати аргументи - значення трьох цілих чисел, розділених комами.

Перше число визначає, з якого значення починає змінюватися змінна циклу for, друге число - значення, до якого може змінюватися змінна циклу for (число не входить у діапазон). Для прикладу, виконавши такий код:

for i in range(12, 16):
    print(i)

отримаємо результати:

12
13
14
15

Також функцію range() можна викликати і з трьома аргументами. Перші два - визначають початок і кінець діапазону зміни значень змінної циклу for, а третій задає крок цієї зміни. Наприклад, виконавши такий код:

for i in range(1, 10, 2):
    print(i)

отримаємо результати:

1
3
5
7
9

Функцію range() можна викликати також, задавши від’ємний крок. У такому випадку значення змінної циклу for буде змінюватися від більших значень до менших. Виконання циклу for з використанням виклику range(3, -4, -1):

for i in range(3, -4, -1):
    print(i)

дає такий результат:

3
2
1
0
-1
-2
-3

6.3.7. Цикл for і послідовності

Цикл for дозволяє проходити по різним структурам даних, які є послідовностями.

Використання циклу for для проходження по елементам списку:

birds = ['pigeon', 'crow', 'owl', 'eagle']
for bird in birds:
    print(bird)

Результат:

pigeon
crow
owl
eagle

Списки, на зразок birds, є прикладом ітеративних об’єктів у Python поряд з рядками, кортежами, словниками.

Прохід (ітерація) по кортежу або списку повертає один елемент за раз.

Ітерація по рядку повертає один символ за раз, як показано на цьому прикладі:

word = 'crab'
for letter in word:
    print(letter)

Результат:

c
r
a
b
Ітерація по словнику у циклі (або з використанням функції keys()) повертає ключі словника.

В наступному прикладі ключі словника є назвами сфер діяльності людини, а значення словника - назвами професій:

professions = {'business': 'economist', 'tv': 'newsreader', 'it': 'web developer', 'education': 'teacher'}
for key in professions: # або for key in professions.keys():
    print(key)

Результат:

business
education
tv
it
Щоб виконувати ітерацію по значенням словника, а не по ключам, необхідно використовувати функцію values().
professions = {'business': 'economist', 'tv': 'newsreader', 'it': 'web developer', 'education': 'teacher'}
for value in professions.values():
    print(value)

Результат:

teacher
newsreader
web developer
economist
Для отримання як ключа, так і значення із словника, ви можете використовувати функцію items(). Результатом буде кортеж із парами «ключ: значення».
professions = {'business': 'economist', 'tv': 'newsreader', 'it': 'web developer', 'education': 'teacher'}
for item in professions.items():
    print(item)

Результат:

('business', 'economist')
('education', 'teacher')
('it', 'web developer')
('tv', 'newsreader')

Можна присвоїти значення кортежу за один крок. Для кожного кортежу, який повертає функція items(), надайте перше значення (ключ) змінній key, а друге (значення) - змінній value:

professions = {'business': 'economist', 'tv': 'newsreader', 'it': 'web developer', 'education': 'teacher'}
for key, value in professions.items():
    print('Category', key, 'has a profession', value)

Результат:

Category business has a profession economist
Category education has a profession teacher
Category it has a profession web developer
Category tv has a profession newsreader

Словник завжди підтримує зв’язок між ключем і пов’язаним з ним значенням, але порядок отримання елементів із словника непередбачуваний.

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

favorite_languages = {'john': 'Python', 'catherine': 'C', 'mary': 'Ruby', 'alex': 'Python'}
for name in sorted(favorite_languages.keys()):
    print(name.title() + ", thank you for taking the poll.")

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

Alex, thank you for taking the poll.
Catherine, thank you for taking the poll.
John, thank you for taking the poll.
Mary, thank you for taking the poll.

6.3.8. Функція zip()

Функція zip() використовується для паралельльної ітерації по декільком послідовностям одночасно.
days = ['Monday', 'Tuesday', 'Wednesday']
fruits = ['coconut', 'lemon', 'mango']
drinks = ['coffee', 'tea', 'fruit juice']
desserts = ['marmalade', 'ice cream', 'pie', 'pudding']
for day, fruit, drink, dessert in zip(days, fruits, drinks, desserts):
    print(day, ": drink", drink, "eat", fruit, "enjoy", dessert)

Результат:

Monday : drink coffee eat coconut enjoy marmalade
Tuesday : drink tea eat lemon enjoy ice cream
Wednesday : drink fruit juice eat mango enjoy pie

Функція zip() припиняє свою роботу, у випадку використання найкоротшої послідовністі. Один зі списків (desserts) виявився довшим за інші, тому ніхто не отримає pudding, поки ми не збільшимо інші списки.

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

Створимо два кортежі з відповідних один одному англійських і французьких слів:

english = 'Monday', 'Tuesday', 'Wednesday'
french = 'Lundi', 'Mardi', 'Mercredi'

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

print(list(zip(english, french)))

Результат:

[('Monday', 'Lundi'), ('Tuesday', 'Mardi'), ('Wednesday', 'Mercredi')]

Якщо передати результат роботи функції zip() безпосередньо функції dict() - у нас готовий невеликий англо-французький словник:

print(dict(zip(english, french)))

Результат:

{'Wednesday': 'Mercredi', 'Monday': 'Lundi', 'Tuesday': 'Mardi'}

6.4. Включення (скорочення синтаксису)

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

6.4.1. Включення для списків

Наприклад, для створення числового списку можна використати код, вказаний нижче:

number_list = []
for number in range(1, 6):
    number_list.append(number)
print(number_list)

У результаті отримаємо такий список:

[1, 2, 3, 4, 5]

Але більш характерно для Python є створення списку за допомогою включення списку. Проста форма такого включення виглядає так:

[вираз for елемент in ітеративний елемент]

Приклад утворення списку цілих чисел через включення списку:

number_list = [number for number in range(1,6)]
print(number_list)

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

[1, 2, 3, 4, 5]

В даному випадку перша змінна number (вона є виразом) формує значення для списку number_list. Друга змінна number є частиною циклу for. Щоб показати, що перша змінна number є виразом, спробуємо такий варіант:

number_list = [number-1 for number in range(1,6)]
print(number_list)

У результаті отримаємо список із значеннями меншими на 1:

[0, 1, 2, 3, 4]

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

[вираз for елемент in ітеративний елемент if умова]

Для прикладу, створимо нове включення, яке створює список, що складається тільки з непарних чисел, розташованих в діапазоні від 1 до 5:

Використовуйте умови number % 2 == 1 або number % 2 == 0 для відбору відповідно непарних або парних чисел.
a_list = [number for number in range(1, 6) if number % 2 == 1]
print(a_list)

У результаті однієї з умов отримуємо список непарних чисел:

[1, 3, 5]

Включення списку з умовою виглядає більш компактно, ніж код, записаний традиційним способом (хоча результати виконання однакові):

a_list = []
for number in range(1, 6):
    if number % 2 == 1:
        a_list.append(number)
print(a_list)

6.4.2. Включення для словників

Для словників також можна створити включення. Найпростіша його форма виглядає так:

{ключ: значення for вираз in ітеративний елемент}

Як і у випадку із включеннями списку, до включення словників також входять перевірки if і оператори for:

word = 'Antarctica'
letter_counts = {letter: word.count(letter) for letter in word}
print(letter_counts)

У циклі, проходячи по кожній із літер у рядку Antarctica, обчислюється скільки разів з’являється поточна буква. Команда word.count(letter) викликається кілька разів (на це витрачається додатковий час), так як літери с, a і t зустрічаються по два рази. Але на вміст словника це не впливає, адже коли проходимо букви с, a або t другий раз, то існуючий запис у словнику замінюється на поточне значення:

{'i': 1, 'c': 2, 'a': 2, 'n': 1, 'r': 1, 'A': 1, 't': 2}

Наступний спосіб розв’язування задачі більш характерний для Python:

word = 'Antarctica'
letter_counts = {letter: word.count(letter) for letter in set(word)}
print(letter_counts)

Ключі словника розміщені в іншому порядку, ніж у попередньому прикладі, але вміст словника той самий:

{'a': 2, 'r': 1, 'i': 1, 'c': 2, 't': 2, 'A': 1, 'n': 1}

Розв’язування попередньої задачі традиційним способом буде таким:

word = 'Antarctica'
letter_counts = {}
for letter in set(word):
    letter_counts[letter] = word.count(letter)
print(letter_counts)

6.5. Генератори

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

У поданому прикладі використовується генератор - функція range(), яка генерує послідовність цілих чисел і обчислюється сума чисел від 1 до 50 включно за допомогою функції sum():

>>> sum(range(1,51))
1275

6.6. Функції

Функції - це іменовані блоки коду, призначені для вирішення однієї конкретної задачі.

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

Функція - це міні-програма у межах основної програми.

6.6.1. Визначення і виклик функцій

Python надає у використання вбудовані функції, на зразок, len(), print(), input() тощо. Але можна створювати і власні функції.

Використання функцій спрощує читання, написання, тестування коду і виправлення помилок.

Створення функцій - це перший крок до повторного використання коду.

Функція може приймати будь-яку кількість будь-яких вхідних параметрів і повертати будь-яку кількість будь-яких результатів.

Функцію можна:

  • визначити

  • викликати

Для визначення функції, необхідно написати def, ім’я функції, вхідні параметри у круглих дужках, двокрапку, після якої з відступом йде блок коду:

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

Cтворимо окремий файл з певним ім’я у вибраному середовищі програмування для наступних прикладів.

Визначимо функцію без вхідних параметрів, яка виводить повідомлення на екран:

def birthday_card():
    print('Happy birthday!')

Для виклику функції необхідно написати її ім’я і круглі дужки: birthday_card(). Коли викликається функція birthday_card(), Python виконує код всередині функції - виводить повідомлення

Happy birthday!

на екран і повертає назад керування основній програмі.

Напишемо ще одну функцію без параметрів, яка повертає значення і викличемо цю функцію в команді if для перевірки значення, яке повертає функція:

# визначення функції
def hungry():
    return True

# виклик функції та перевірка умови
if hungry():
    print('Eat oat flakes!')
else:
    print('Drink water.')

Така комбінація функції з перевіркою умови виведе повідомлення Eat oat flakes!.

Тепер визначимо функцію echo(), яка має один параметр anything. Дана функція використовує оператор return, щоб відправити результат виконання у зовнішню основну програму (звідки викликається функція):

# визначення функції
def echo(anything):
    return anything

Викличемо функцію echo(), передавши їй рядок 'I enjoy travelling!':

Функція print() використовується для друкування на екрані значення, яке повернула функція оператором return.
print(echo('I enjoy travelling!'))

Отримаємо такий результат:

I enjoy travelling!
Значення, які передаються у функцію при виклику, називаються аргументами. Коли викликається функція з аргументами, значення цих аргументів копіюються у відповідні параметри всередині функції.

В нашому прикладі функції echo() передавалось одне значення - рядок 'I enjoy travelling!'. Це значення копіювалось всередині функції echo() у параметр anything, а потім поверталось у зовнішню програму, звідки викликалась функція.

Визначимо ще одну функцію, яка повертає повідомлення, в залежності від обраного кольору:

def like(interest):
    if interest == 'yoga':
        return "I quite like " + interest + "."
    elif interest == "football":
        return "Yes, I play " + interest + "."
    elif interest == 'guitar':
        return "Yes, I play the " + interest + "."
    else:
        return "I'm interested in " + interest + "."
story = like('photography')
print(story)
story = like('yoga')
print(story)
story = like('football')
print(story)
story = like('guitar')
print(story)

Функція like() виконає такі дії:

  • параметру функції interest присвоїть значення 'photography' ('yoga', 'football', 'guitar')

  • пройде по ланцюжку if-elif-else

  • поверне рядок з повідомленням

  • присвоїть повернений рядок змінній story

А результатом виконання команд print(story) будуть такі повідомлення:

I'm interested in photography.
I quite like yoga.
Yes, I play football.
Yes, I play the guitar.

6.6.2. Значення None

Для прикладу, визначимо функцію, яка не має параметрів:

def do_nothing():
    pass
Вираз pass вказує Python, що функція нічого не робить.

Викличемо дану функцію:

do_nothing()

Функція do_nothing() відпрацює, але нічого не виведе на екран.

Викличемо цю ж функцію з використанням функції print():

print(do_nothing())

У результаті отримаємо значення:

None
Якщо функція не використовує оператор return явно, вона повертає результат None.

None - це спеціальне значення в Python, яке означає нічого або порожнє місце, коли функція нічого не повертає.

Значення None не є булевим значенням False.

Напишемо невелику функцію, яка виводить перевірку на рівність None:

def is_none(thing):
    if thing is None:
        print("It's None")
    elif thing:
        print("It's True")
    else:
        print("It's False")

і виконаємо кілька перевірок:

is_none(None)  # It's None
is_none(True)  # It's True
is_none(False) # It's False
is_none(0)     # It's False
is_none(0.0)   # It's False
is_none(())    # It's False
is_none([])    # It's False
is_none({})    # It's False
is_none(set()) # It's False

6.6.3. Позиційні та іменовані аргументи

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

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

Визначимо функцію, яка створює і повертає словник з вхідних позиційних аргументів:

def music(terminology, musician, genre):
    return {'terminology': terminology, 'musician': musician, 'genre': genre}

В даному випадку, у визначенні функції music, значення у круглих дужках terminology, musician, genre - це параметри функції.

Викличемо функцію командою print(music('tune', 'guitarist', 'blues')). Значення tune, guitarist, blues у виклику функції і є позиційними аргументами, які будуть скопійовані у відповідні параметри функції і вона поверне словник (порядок у словнику непередбачуваний):

{'genre': 'blues', 'terminology': 'tune', 'musician': 'guitarist'}

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

print(music(terminology='tune', musician='guitarist', genre='blues'))

У даному виклику функції music: musician - це ім’я аргументу, а 'guitarist' - значення аргументу. Аналогічно і для інших імен і значень аргументів. У результаті словник набуде такого вигляду:

{'terminology': 'tune', 'musician': 'guitarist', 'genre': 'blues'}

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

print(music('tune', musician='guitarist', genre='blues'))

Результат виведення:

{'terminology': 'tune', 'genre': 'blues', 'musician': 'guitarist'}

6.6.4. Значення за замовчуванням

Для кожного параметра функції можна визначити значення за замовчуванням.

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

Визначимо функцію, яка повертає інформацію про домашнього улюбленця:

def describe_pet(pet_name, animal_type='parrot'):
    print("I have a " + animal_type + ".")
    print("My " + animal_type + "'s name is " + pet_name.title() + ".")

У визначення функції describe_pet() включений параметр animal_type із значенням за замовчуванням 'parrot'. Якщо тепер функція буде викликана з іменованим аргументом pet_name='Jack Sparrow', але без аргумента animal_type

describe_pet(pet_name='Jack Sparrow')

Python знатиме, що для параметра animal_type слід використовувати значення 'parrot':

I have a parrot.
My parrot's name is Jack Sparrow.

Для виведення інформації про будь-яку іншу домашню тварину, окрім папуги, використовується виклик функції describe_pet() наступного виду:

describe_pet(pet_name='peppa', animal_type='guinea pig')

Так як аргумент для параметра animal_type у виклику функції заданий явно ('peppa'), Python ігнорує значення параметра за замовчуванням:

I have a guinea pig.
My guinea pig's name is Peppa.

6.6.5. Використання аргументів з символами * і **

Якщо символ * буде використаний усередині функції з параметром, довільна кількість позиційних аргументів буде згрупована у кортеж. В наступному прикладі визначення функції print_days()

def print_days(*args):
    print('Get ready:', args)

args є кортежем параметрів. Всі аргументи, які ви передасте у функцію print_days()

print_days('Wednesday', 'Thursday', 'Friday', 'Weekend...')

будуть виведені на екран як кортеж args:

Get ready: ('Wednesday', 'Thursday', 'Friday', 'Weekend...')

Це корисно при написанні функцій, на зразок print(), які приймають довільну кількість аргументів. Наприклад, якщо у функції

def print_travel(required1, required2, *args):
    print('For train travel is required:', required1)
    print('For train travel is required too:', required2)
    print('All the rest:', args)

є також обов’якові параметри (в даному випадку required1 і required2), у які будуть скопійовані значення позиційних аргументів ('return ticket', 'train') при виклику функції

print_travel('return ticket', 'train', 'carriage', 'seat', 'luggage rack')

то кортеж *args отримає всі інші аргументи, починаючи із значення 'carriage':

For train travel is required: return ticket
For train travel is required too: train
All the rest: ('carriage', 'seat', 'luggage rack')

Ви можете використовувати два символа **, щоб згрупувати іменовані аргументи у словник, де імена аргументів стануть ключами, а їх значення - відповідними значеннями у словнику. У наступному прикладі визначається функція print_character()

def print_character(**kwargs):
    print('Person\'s characteristics:', kwargs)

при виклику якої з іменованими аргументами

print_character(emotions='cheerful', sense='happy', look='slim')

виводяться її іменовані аргументи у вигляді словника:

Person's characteristics: {'emotions': 'cheerful', 'look': 'slim', 'sense': 'happy'}
При використанні символа * не потрібно обов’язково називати кортеж параметрів args, аналогічно при використанні символів ** називати словник kwargs, однак це поширена практика у Python.

6.6.6. Простір імен і області видимості

Програми в Python можуть мати різні простори імен - розділи, всередині яких певне ім’я унікальне і не пов’язане з такими ж іменами в інших просторах імен.

Кожна функція визначає власний простір імен. Якщо ви визначите змінну, яка називається a в основній програмі, і іншу змінну a в окремій функції, вони будуть посилатися на різні значення. Але, якщо необхідно, ви можете отримати доступ до імен інших просторів імен різними способами.

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

Значення глобальної змінної можна отримати всередині функції. Наприклад, визначимо глобальну змінну animal і функцію print_global():

animal = 'kangaroo'
def print_global():
    print('inside print_global:', animal)

Використавши функцію print() і виклик функції print_global()

print('at the top level:', animal)
print_global()

отримаємо значення змінної animal в обох випадках:

at the top level: kangaroo
inside print_global: kangaroo

Але якщо спробувати отримати значення глобальної змінної і змінити його всередині функції

def change_and_print_global():
    print('inside change_and_print_global:', animal)
    animal = 'giraffe'
    print('after the change:', animal)
change_and_print_global()

отримаємо помилку:

...
UnboundLocalError: local variable 'animal' referenced before assignment

Розглянемо програму з іншою функцією, яка містить змінну з такою ж назвою, що і глобальна змінна animal:

animal = 'kangaroo' (1)
def change_local():
    animal = 'giraffe' (2)
    print('inside change_local:', animal, id(animal)) (3)
change_local()
print(animal)
print(id(animal)) (4)

В результаті виконання програми отримаємо:

inside change_local: giraffe 47293440
kangaroo
47287896
  1. Ми присвоїли рядок 'kangaroo' глобальній змінній з іменем animal.

  2. Функція change_local() також має змінну animal, але ця змінна знаходиться в локальному просторі імен функції.

  3. Ми використали функцію id(), щоб вивести на екран унікальне значення об’єкта animal всередині функції.

  4. Ми використали функцію id(), щоб вивести на екран унікальне значення об’єкта animal ззовні функції.

Порівнюючи результати роботи функції id(), можна зробити висновок: змінна animal, розміщена всередині функції change_local() - це не змінна, що знаходиться на рівні основної програми.

Щоб змінна всередині функції була видимою в основній програмі, потрібно явно використовувати ключове слово global. Перепишемо попередню програму таким чином

animal = 'kangaroo'
def change_and_print_global():
    global animal
    animal = 'giraffe'
    print('inside change_and_print_global:', animal)
print(animal) (1)
change_and_print_global() (2)
print(animal) (3)

і, виконавши її, отримаємо наступні результати:

kangaroo (1)
inside change_and_print_global: giraffe (2)
giraffe (3)
  1. До виклику функції change_and_print_global() глобальна змінна animal мала значення 'kangaroo'.

  2. Виклик функції change_and_print_global() з використанням ключового слова global у функції перетворив локальну змінну animal на глобальну і переписав її значення з 'kangaroo' на 'giraffe'.

  3. Тепер змінна animal в основній програмі має значення 'giraffe'.

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

Python надає дві функції для доступу до вмісту ваших просторів імен:

  • locals() - повертає словник, що містить імена локального простору імен

  • globals() - повертає словник, що містить імена глобального простору імен

Ці функції використовуються так:

animal = 'kangaroo' # глобальна змінна
def change_local():
    animal = 'giraffe' # локальна змінна
    print('locals:', locals())
print(animal)
change_local()
print('globals:', globals())
print(animal)

Результат використання цих функцій:

kangaroo
locals: {'animal': 'giraffe'}
globals: {
	...,
	'__name__': '__main__',
	...
	'animal': 'kangaroo',
	...
	'__builtins__': <module 'builtins' (built-in)>}
kangaroo

Як бачимо з прикладу, локальний простір імен всередині функції change_local() містить лише локальну змінну animal зі значенням 'giraffe'. Глобальний простір імен містить окрему змінну animal зі значенням 'kangaroo' і багато інших змінних, імена яких починаються і закінчуються з __.

У глобальному порсторі імен globals, наприклад, основній програмі присвоєно спеціальне ім’я __main__, обрамлене по обидва боки символами __. Такі імена заразервовані для використання всередині Python, тому їх використання для імен власних змінних неприпустимі.

6.7. Обробка помилок

6.7.1. Виняткові ситуації

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

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

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

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

Можна також створювати власні об’єкти винятків.

6.7.2. Блок try-except

Якщо не використовувати обробники винятків, Python виведе повідомлення про помилку і деяку інформацію про те, де сталася помилка, а потім завершить програму, як показано в наступному фрагменті коду:

short_list = [1, 2, 3]
position = 5
print(short_list[position])

Виникає помилка виходу за межі діапазону списку:

Traceback (most recent call last):
  File "C:\Python34\test.py", line 246, in <module>
    print(short_list[position])
IndexError: list index out of range

Розмістимо свій код у блоці try і використаємо блок except, щоб обробити помилку:

short_list = [1, 2, 3]
position = 5
try:
    short_list[position]
except:
    print('Need a position between 0 and', len(short_list)-1, 'but got', position)

В результаті виконання програми отримаємо повідомлення:

Need a position between 0 and 2 but got 5

Код у даному прикладі запускається всередині блока try. Якщо виникла помилка, генерується виняток і виконується код, розміщений всередині блока except. Якщо помилки не виникають в процесі виконання програми, блок except буде не задіяний.

В даному випадку використання блока except говорить лише: «Виникла помилка!». Якщо ви припускаєте, що в програмі може статися помилка і знаєте назву винятку, який при цьому згенерується, напишіть блок try-except для обробки відомого винятка.

Для прикладу, розглянемо виконання операції

print(5/0)

яка викликає помилку ділення на нуль, при якій Python ініціює виняток ZeroDivisionError:

Traceback (most recent call last):
  File "C:\Python34\test.py", line 266, in <module>
    print(5/0)
ZeroDivisionError: division by zero

Помилка ZeroDivisionError - є об’єктом винятку. Повідомим Python, як слід чинити при виникненні даного винятку, щоб програма буде підготовлена до його появи. Ось як виглядає блок try-except для обробки винятків ZeroDivisionError:

try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")

У цьому прикладі код блоку try породжує помилку ZeroDivisionError, тому Python шукає блок except з описом того, як слід діяти в такій ситуації. При виконанні коду блоку except користувач бачить зрозуміле повідомлення про помилку:

You can't divide by zero!

Інколи необхідно отримати не лише об’єкт винятку, а зберегти його у змінну, використовуючи таку форму запису:

try:
    блок коду
except тип_винятку as назва_змінної:
    блок коду, коли виникла помилка

У наступному прикладі виконується перевірка на IndexError, оскільки саме цей виняток викликається, коли ви вказуєте недійсну (відсутню) позицію у послідовності. Виняток IndexError зберігається у змінній err, а будь-який інший виняток Exception - у змінній other.

predators = ['tiger', 'wolf', 'bear']
while True:
    value = input('Position [q to quit]? ')
    if value == 'q':
        break
    try:
        position = int(value)
        print(predators[position])
    except IndexError as err:
        print('Bad index:', position)
    except Exception as other:
        print('Something else broke:', other)

У прикладі на екран виводиться все, що зберігається в змінній other:

Position [q to quit]? 1
wolf
Position [q to quit]? 0
tiger
Position [q to quit]? 2
bear
Position [q to quit]? 3
Bad index: 3
Position [q to quit]? 2
bear
Position [q to quit]? two
Something else broke: invalid literal for int() with base 10: 'two'
Position [q to quit]? q

Введення позиції 3, як і очікувалося, генерує виняток IndexError. Введення слова two «не сподобалось» функції int(), яка була оброблена у другому обробнику винятку.

Є ще дві інструкції, пов’язані з блоком try-except, це finally і else.

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

Інструкція else виконується в тому випадку, якщо виняток не буде згенерований.

Наприклад, при виконанні фрагмента коду

def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("Division by zero!")
    else:
        print("Result is", result)
    finally:
        print("End of the calculation.")
divide(2, 1)
divide(2, 0)

отримаємо наступні результати:

Result is 2.0
End of the calculation.
Division by zero!
End of the calculation.
Переглянути список винятків, які генеруються у разі появи помилок, можна на сайті офіційної документації Python.

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

Для визначення власних винятків необхідно використовувати класи, які описуються у розділі 10.

Будь-який виняток є класом, зокрема, нащадком класу Exception. Створимо виняток, який називається IsNotTitleException. Викличемо виняток, коли зустрінемо слово у списку, перша літера якого записана у нижньому регістрі:

>>> class IsNotTitleException(Exception):
...     pass
...
>>> rooms = ['Kitchen', 'study', 'Hall', 'Bathroom']
>>> for room in rooms:
...     if room.title() != room:
...         raise IsNotTitleException(room)
...
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
__main__.IsNotTitleException: study

Поведінку для винятку IsNotTitleException не визначено (використано pass). Тому батьківський клас Exception самостійно виконав виведення повідомлення на екран при генерації винятку. Але можна вказати, що має виводити власний виняток, коли його примусово викликати за допомогою ключового слова raise:

>>> class IsNotTitleException(Exception):
...     pass
...
>>> try:
...     raise IsNotTitleException('Fault!')
... except IsNotTitleException as exc:
...     print(exc)
...
Fault!

6.8. Завдання

6.8.1. Контрольні запитання

  1. Які два значення має булевий тип даних?

  2. Назвіть три булевих оператори і шість операторів порівняння.

  3. Напишіть кілька прикладів із таблиць істинності.

  4. Яка різниця між операторами порівняння і присвоювання?

  5. Що таке вказівка розгалуження і де вона використовується?

  6. Наведіть приклади виразів, що можуть використовуватись як умова.

  7. Яке сполучення клавіш використовується для виходу з нескінченного циклу?

  8. Чим відрізняються виклики функцій range(15), range(0, 15), range(0, 15, 1) у циклі for?

  9. Навіщо використовувати у програмах функції?

  10. Коли виконується код функції: коли вона визначається, чи коли вона викликається?

  11. З допомогою якої конструкції створюються функції?

  12. Чим відрізняється визначення функції від її виклику?

  13. Скільки глобальних областей видимості може мати програма на Python? Скільки локальних?

  14. Що таке значення, яке повертає функція? Чи може бути це значення у вигляді виразу?

  15. Що відбувається із змінними, які знаходяться у локальній області видимості, при поверненні них із функції?

  16. Яке значення повертає функція, якщо у ній відсутній оператор return?

  17. Як змусити змінну у функції посилатися на глобальну змінну?

  18. Що таке тип даних None?

  19. Як можна запобігти аварійному завершенні програми?

  20. Які блоки коду записують у блок try? А які у except?

6.8.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

  1. Які результати обчислення таких виразів?

(15 > 8) and (2 == 9)
not (2 > 1)
(6 > 3) or (8 == 7)
not ((9 > 4) or (3 == 4))
(True and True) and (True == False)
(not False) or (not True)
  1. Напишіть код, який виводить різні повідомлення, в залежності від значення, що зберігається у змінній weather_forecast: What a beautiful day!, якщо значення змінної дорівнює sun, Take an umbrella!, якщо значення змінної дорівнює rain і The sun’s just gone in! – у іншому випадку.

  2. Створіть програму, яка виводить повідомлення про кількість очок, які отримав гравець у комп’ютерній грі. Створіть змінну з ім’ям points_color і надайте їй значення 'green', 'yellow' або 'red'. Якщо змінна points_color містить значення 'green', виведіть повідомлення про те, що гравець отримав 5 очок, якщо 'yellow' - 10 очок, якщо 'red', виведіть повідомлення про те, що гравець отримав 15 очок.

  3. Напишіть ланцюжок if-elif-else для визначення віку людини. Надайте значення змінній age, а потім виведіть повідомлення. Якщо значення age менше 2 - baby, якщо значення age більше або дорівнює 2, але менше 4 - kid, якщо значення age більше або дорівнює 4, але менше 13 - child, якщо значення age більше або дорівнює 13, але менше 20 - teenager. якщо значення age більше або дорівнює 20, але менше 65 - grown-up, якщо значення age більше або дорівнює 65 - senior.

  4. Створіть список трьох своїх улюблених фруктів і назвіть його favorite_fruits. Напишіть перевірку на те, чи входить фрукт у список. Якщо фрукт входить у список, виводиться повідомлення, на зразок You really like peaches!, у протилежному випадку - повідомлення про відсутність фрукту у списку.

  5. Використайте цикл for, щоб вивести на екран в окремих рядках значення списку [5, 4, 3, 2, 1, 0, 'GO!'].

  6. Використайте функцію zip(), щоб створити словник movies, який об’єднує у пари списки: seasons = ['summer', 'autumn'] і months = [ 'july', 'november']. Виведіть вміст словника.

  7. Використайте відомі вам структури коду для виведення ключів і значень словника activity = {'business': 'manager', 'it': 'software developer', 'science': 'scientist'} у вигляді, на зразок «категорія: професія».

  8. Використайте включення списку, щоб створити список, який містить парні числа у діапазоні range(12).

  9. Використайте включення словника, щоб створити словник squares з ключами у вигляді цілих чисел з діапазону range(1, 11). Значення словника формуються піднесенням ключів до другого степеня.

  10. Використайте цикл for для виведення чисел від 1 до 15 включно, в один рядок і пропусками між ними.

  11. Створіть список чисел від 1 до 1 000 000. Скористайтеся функціями min() і max() та переконайтеся у тому, що список дійсно починається 1 і закінчується 1 000 000. Викличте функцію sum() і подивіться, наскільки швидко Python зможе підсумувати мільйон чисел.

  12. Cкористайтеся третім аргументом функції range() для створення списку непарних чисел від 1 до 25 і виведіть усі числа в окремих рядках у циклі for.

  13. Створіть список перших 10 кубів (тобто кубів усіх цілих чисел від 1 до 10) і виведіть значення усіх кубів у циклі for в один рядок з пропусками.

  14. Cтворіть список чисел у діапазоні від 3 до 60 і виведіть усі числа списку у циклі while в окремих рядках.

  15. Визначте функцію trees, яка повертає список ['poplar', 'willow', 'lime']. Викличте функцію.

  16. Напишіть функцію favorite_book(), яка отримує один параметр title. Функція повинна виводити повідомлення, на зразок One of my favorite books is "The Lord of the Rings".. Викличте функцію і переконайтеся у тому, що назва книги правильно передається як аргумент при виконанні функції.

  17. Напишіть функцію make_shirt(), яка отримує розмір футболки і текст, який повинен бути надрукований на ній. Функція повинна виводити повідомлення з розміром і текстом. Викличте функцію з використанням позиційних аргументів. Викличте функцію вдруге з використанням іменованих аргументів. Змініть функцію make_shirt(), щоб футболки за замовчуванням мали розмір XL, і на них виводився текст I love Python!. Створіть футболку з розміром XL і текстом за замовчуванням.

  18. Напишіть функцію city_country(), яка отримує назву міста і країну. Функція повинна повертати рядок у форматі Kiev, Ukraine. Викличте функцію як мінімум для трьох пар «місто-країна».

  19. Визначте виняток, який називається MyException. Згенеруйте його, щоб побачити, що станеться. Потім напишіть код, що дозволяє перехопити цей виняток і вивести рядок This is my exception..

  20. При введенні числових даних часто зустрічається типова проблема: користувач вводить текст замість чисел. При спробі перетворити дані в int генерується виняток TypeError. Напишіть функцію, яка приймає два числа, шукає їх суму і виводить результат. Перехопіть виняток TypeError, якщо будь-яке із вхідних значень не є числом, і виведіть зручне повідомлення про помилку. Протестуйте функцію: спочатку введіть два числа, а потім введіть текст замість одного з чисел.

6.8.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

  1. Виведіть вітальне повідомлення для кожного користувача після його входу на сайт. Cтворіть список з кількох імен користувачів, включаючи ім’я 'Admin'. Перебираючи елементи списку, виведіть повідомлення для кожного користувача. Для користувача з ім’ям 'Admin' виведіть особливе повідомлення - наприклад: Hello Admin, I hope you’re well.. У інших випадках виводиться універсальне привітання - наприклад: Hello Alex, thank you for logging in again.. Додайте команду if, яка перевірить, що список користувачів не порожній. Якщо список порожній, виведіть повідомлення: We need to find some users!. Видаліть зі списку всі імена користувачів і переконайтеся у тому, що програма виводить правильне повідомлення.

  2. Визначте назву геометричної фігури за введеною кількістю її сторін. Програма повинна підтримувати фігури від 3 до 6 сторін. Якщо введена кількість сторін поза межами цього діапазону, програма повинна відображати відповідне повідомлення.

  3. Порядкові числівники у англійській мові закінчуються суфіксом th (окрім 1st, 2nd і 3rd). Cтворіть список чисел від 1 до 9. Використайте ланцюжок if-elif-else у циклі для виведення правильного закінчення числівника для кожного числа. Програма повинна виводити числівники 1st 2nd 3rd 4th 5th 6th 7th 8th 9th, кожен у новому рядку.

  4. Зчитайте ціле число введене користувачем і виведіть повідомлення про те, чи число парне або непарне.

  5. Зчитайте назву місяця від користувача як рядок і виведіть кількість днів у вказаному місяці. Врахувати те, що «February» може мати 28 або 29 днів.

  6. Потрібно визначити, чи є даний рік високосним. Нагадаємо, що високосними роками вважаються ті роки, порядковий номер яких або кратний 4, але при цьому не кратний 100, або кратний 400 (наприклад, 2000-й рік був високосним, а 2100-й буде невисокосним роком). Програма повинна коректно працювати на числах від 1900 до 3000 років. Виведіть Leap year. у разі, якщо рік є високосним і Ordinary year. у протилежному випадку.

  7. Зчитайте зі стандартного вводу цілі числа, по одному числу у рядку, і після першого введеного нуля виведіть суму отриманих на вхід чисел.

  8. Зчитайте цілі числа, які вводить користувач, по одному числу у рядку. Для кожного введеного числа виконайте перевірку: якщо число менше 10, то пропускаємо це число, якщо число більше 100, то припиняємо зчитувати числа. У інших випадках вивести це число в окремому рядку.

  9. Напишіть простий калькулятор, який зчитує три рядки, які вводить користувач: перше число, друге число і операцію, після чого застосовує операцію до введених числах («перше число» «операція» «друге число») і виводить результат на екран. Підтримувані операції: +, -, /, *, mod, pow, div, де mod - це взяття залишку від ділення, pow - піднесення до степеня, div - цілочисельне ділення. У випадку ділення на 0, необхідно використати обробник винятку, який має вивести повідомлення Division by zero!.

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

  11. Створіть програму, яка зчитує позицію шахової фігури від користувача і повідомляє про колір квадрату, де знаходиться фігура. Наприклад, якщо користувач вводить a і 1, то програма повинна повідомити, що квадрат має колір black, якщо d і 5 - програма повідомляє, що квадрат має колір white.

Шахова дошка
До завдання 11: шахова дошка
  1. Чи є рядок паліндромом? Рядок є паліндромом, якщо він однаково читається зліва направо та справа наліво. Наприклад, слова Level, Noon, Anna є паліндромами, незалежно від регістру літер. Рядки, які треба перевірити, вводяться користувачем. Введення даних можна перервати, якщо ввести слово escape. Програма повинна вивести результат перевірки у вигляді повідомлення is a palindrome або is not a palindrome.

  2. Створіть таблицю множення. Розмірність таблиці (кількість рядків і стовпців) вводиться з клавіатури.

  3. Як відомо, День програміста припадає на 256 день року, у невисокосний рік це 13 вересня, а у високосний - 12. Дізнайтеся число і номер місяця, що припадають на день за номером n, який вводиться користувачем, у невисокосному 2017 році.

  4. Конвертуйте десяткове число (з основою 10) у бінарне (двійкове, з основою 2). Користувач вводить десяткове число як ціле, а потім використовується алгоритм поділу (псевдокод), показаний нижче, щоб виконати перетворення. Коли алгоритм завершується, результат містить двійкове подання числа. Програма має вивести відповідне повідомлення, на зразок: 2018 in Decimal is 11111100010 in Binary. Модифікуйте програму таким чином, щоб вона виконувала зворотну операцію: перетворювала двійкове чило у десяткове.

Нехай result буде порожнім рядком
Нехай q представляє ціле число для перетворення
Повторити
	Встановити r рівним остачі, коли q ділиться на 2
	Перетворити r у рядок та додати його на початку до result
	Розділити q на 2, відкидаючи будь-який залишок, і зберегти результат назад у q
Поки q не дорівнює 0
Вивести повідомлення про результат переведення
  1. Реалізуйте шифр Цезаря. Один з перших відомих прикладів шифрування був використаний Юлієм Цезарем. Цезар надавав письмові вказівки своїм генералам, але він не бажав, зрозуміло, щоб про них знали вороги. У результаті він створив власне шифрування, що згодом стало відоме як шифр Цезаря. Ідея цього шифру проста (і, як наслідок, вона не забезпечує серйозного захисту). Кожна буква в оригінальному повідомленні зміщується на 3 місця. В результаті A стає D, B стає E, C стає F, D стає G і т. д. Останні три букви у алфавіті повертаються до початку: X стає A, Y стає B, а Z стає C. Нелітерні символи не враховуються шифром. Користувач вводить з клавітури повідомлення та зсув, а потім програма відображає зашифроване повідомлення. Переконайтеся, що програма шифрує як великі та малі літери. Програма також повинна підтримувати значення негативних зсувів, щоб її можна було використовувати як для кодування повідомлень, так і для декодування повідомлень. У нагоді стануть такі функції: ord() (перетворює символ у номер позиції цього символу у таблиці ASCII) і chr() (перетворює номер позиції символу у таблиці ASCII у відповідний символ).

  2. У програмі визначте функцію, яка генерує випадковий пароль. Пароль має бути довільної довжини від 7 до 10 символів. Кожен символ паролю повинен бути випадково обраним з позицій 33 до 126 з таблиці ASCII. Функція повертає випадково сформований пароль як єдиний її результат і він виводиться в основній програмі. Скористайтеся вказівками з попередньої задачі.

  3. Напишіть програму, у якій комп’ютер генерує випадкове число, а користувач повинен вгадати число, вводячи його з клавіатури за вказану кількість спроб.

  4. Створіть програму за мотивами відомої гри «Камінь, Ножиці, Папір». Застосуйте функціональний підхід при написанні коду програми.

7. Модулі і пакети

Будь-якій програмі на мові Python доступний базовий набір вбудованих функцій, в число яких входять такі функції як print(), input(), len() тощо, які можна виокористовувати у програмі, як кажуть, «з коробки».

Крім того, у Python входить набір модулів, який називається стандартною бібліотекою.

Наприклад, модуль math містить математичні функції, модуль random - функції для роботи з випадковими числами.

7.1. Імпорт модулів: інструкція import

Щоб використовувати функції, які входять у модуль, необхідно його імпортувати (підключити) у програму за допомогою інcтрукції import.

Звичайна форма використання інструкції import:

import модуль

де модуль - це ім’я іншого файлу Python без розширення .py.

Як тільки модуль імпортований, ви можете використовувати будь-яку функцію, яка входить до його складу. Для прикладу, перевіримо як працює модуль random, який надає доступ до функції randint(). Введіть поданий нижче код у файл і збережіть з певним ім’ям:

import random
for i in range(5):
    print(random.randint(1, 10))
Функція randint() повертає випадкове ціле число з діапазону між двома цілими числами, які передаються у функцію в якості аргументів.

Виконавши програму, ви повинні отримати п’ять випадкових цілих чисел з діапазону від 1 до 10 включно:

7
6
2
3
6
Оскільки функція randint() знаходиться у модулі random, то ім’я модуля повинне вказуватися у вигляді префікса (через точку) перед іменем функції, щоб Python міг визначити, що дану функцію слід шукати у модулі random.

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

import модуль1, модуль2, модуль3,...

або імпортують у такому вигляді:

import модуль1
import модуль2
import модуль3, модуль4
Всі інструкції import рекомендують розміщувати у верхній частині файлу.

Тепер можна використовувати будь-які функції, які знаходяться у цих модулях.

Оператор import має альтернативну форму використання:

from модуль import функція1, функція2,...

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

from random import randint, random
for i in range(5):
    print(randint(1, 10))
print(random())
8
7
3
6
10
0.12147106708747124 # результат виконання функції random()
Функція random() викликається без аргументів і генерує випадкове дійсне число від 0 до 1 не включаючи меж даного діапазону.
Ім’я функції, перед яким записане ім’я модуля, наприклад, random.randint(), використовувати безпечніше. Тому краще всього користуватися звичайною формою інструкції import: import модуль.

При імпортуванні можна використовувати символ *. Для нашого прикладу з модулем random():

from random import *

цей запис можна прочитати так: з модуля random() імпортувати все.

Для скорочення назв імпортованих модулів (або функцій із модулів) можна також користуватися псевдонімами. Форма запису при такому використанні:

import модуль as псевдонім # псевдонім для модуля
from модуль import функція as псевдонім # псевдонім для функції

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

import random as rn
for i in range(5):
    print(rn.randint(1, 10))

7.2. Модуль __main__

У файлах Python часто розміщують таку конструкцію:

...
if __name__ == '__main__':
    блок коду

Давайте розберемось як ця інструкція працює. Для прикладу, визначимо функцію у файлі users.py:

def greet_users(names):
    """Виведення привітання для кожного користувача у списку."""
    for name in names:
        message = "Hello, " + name.title() + "!"
        print(message)
usernames = ['alex', 'jack', 'anna']
greet_users(usernames)

Дана функція отримує список імен користувачів і формує вітальні вітання для кожного із них:

Hello, Alex!
Hello, Jack!
Hello, Anna!

Файл users.py вважатимемо модулем, який містить єдину функцію. Створимо ще один файл main_file.py і помістимо у нього такий код:

from users import greet_users (1)
print('This code is executed!') (2)
if __name__ == '__main__': (3)
    print('This code is executed because the main_file.py is not being imported!')
  1. У файл main_file.py виконується імпорт функції greet_users() із модуля-файлу users.py.

  2. Цей рядок коду виконується як при запуску файлу main_file.py, так і у випадку, коли файл main_file.py імпортуємо у інший файл another_file.py (у даному випадку файл main_file.py стає модулем) таким чином:

import main_file
  1. Умова if і відповідний блок коду виконуються лише у випадку, коли ми запукаємо на виконання сам файл main_file.py.

Результати окремого виконання кожного із файлів у терміналі, наприклад Windows, будуть такими:

C:\Python34>python users.py (1)
Hello, Alex!
Hello, Jack!
Hello, Anna!

C:\Python34>python main_file.py (2)
Hello, Alex!
Hello, Jack!
Hello, Anna!
This code is executed!
This code is executed because the main_file.py is not being imported!

C:\Python34>python another_file.py (3)
Hello, Alex!
Hello, Jack!
Hello, Anna!
This code is executed!
  1. Запуск файлу users.py із функцією, яка повертає результи своєї роботи.

  2. Запуск файлу main_file.py, у який імпортували функцію з файлу users.py. Конструкція if __name__ == '__main__': виконується.

  3. Запуск файлу another_file.py, у який імпортували код файлу main_file.py. Конструкція if __name__ == '__main__': не виконується.

7.3. Аргументи командного рядка

При запуску програми на Python, яка збережена у файлі з певним ім’ям, у термінальму вікні (вікні командного рядка) необхідно ввести python і ім’я цього файлу.

Створимо файл test.py, який міститиме наступні рядки:

import sys
print('Program arguments:', sys.argv)

Тепер запустимо цей файл у термінальному вікні, наприклад, Windows, перед тим перейшовши у папку, де файл був збережений:

C:\Python34>python test.py
Program arguments: ['test.py']

C:\Python34>python test.py tra la la
Program arguments: ['test.py', 'tra', 'la', 'la']

Змінна argv з модуля sys містить список аргументів командного рядка. Значення argv[0] - ім’я файлу, який запускається (або повний шлях до нього). argv[1], argv[2] і т.д. - це інші аргументи командного рядка.

В нашому випадку argv[0] має значення test.py, argv[1] має значення tra, argv[2] має значення la, argv[3] має значення також la.

7.4. Пакети

Модулі Python ораганізовують в групи файлів, які називаються пакетами.

Пакет - це каталог, який містить файл __init__.py, файли модулів та інші підпакети.

7.4.1. Створення власних пакетів

Створимо власний пакет на прикладі такої задачі. Наприклад, нам потрібно дізнатися різні типи прогнозів погоди: на наступний день і на наступний тиждень. Створимо папку boxes, а у ній папку sources, яка буде містити два модуля: файли daily.py і weekly.py. Кожний з них міститиме функцію forecast(). Версія файлу погоди на кожний день повертатиме рядок, а версія файлу погоди на кожен тиждень повертатиме список із 7 рядків.

Розглянемо основну програму і два модуля в операційній системі Windows.

Функція enumerate() розбиває список на частини і відправляє кожний елемент списку в цикл for, додаючи до кожного елемента число - порядковий номер, починаючи з 1.

Основна програма буде міститися у файлі по шляху boxes\weather.py:

from sources import daily, weekly
print("Daily forecast:", daily.forecast())
print("Weekly forecast:")
for number, outlook in enumerate(weekly.forecast(), 1):
	print(number, outlook)

Модуль 1 буде знаходитися у файлі по такому шляху: boxes\sources\daily.py:

def forecast():
    'fake daily forecast'
    return 'like yesterday'

Модуль 2 буде знаходитися у файлі по такому шляху: boxes\sources\weekly.py:

def forecast():
    """Fake weekly forecast"""
    return ['mist', 'strong winds', 'sleet', 'freezing rain', 'rain', 'fog', 'drizzle']
У модулях 1 і 2 присутні коментарі 'fake daily forecast' і """Fake weekly forecast""" відповідно, створені за допомогою різних типів лапок. Інтерпретатор ігнорує рядки з цими коментарями при виконанні програми.

В папці sources необхідно розмістити файл __init__.py. Цей файл може бути порожнім, але він потрібний для того, щоб Python міг вважати папку, яка його містить, пакетом.

Виконання основної програми weather.py дасть такий результат:

Daily forecast: like yesterday
Weekly forecast:
1 mist
2 strong winds
3 sleet
4 freezing rain
5 rain
6 fog
7 drizzle

7.4.2. Використання пакету pip

Пакет pip - це система управління пакетами і найпопулярніший спосіб встановити сторонні (нестандартні) пакети Python.

Починаючи з версії мови 3.4, pip є стандартною частиною Python.

Відкрийте термінальне вікно і введіть команду:

pip3 help

На екрані з’явиться інформація про команди pip та їх використання. Наведемо приклади використання деяких з них у термінальному вікні.

Для оновлення самого pip використовують команду:

pip3 install --upgrade pip

Для перегляду списку установлених пакетів використовують команду:

pip3 list

Для установки пакету використовують команду:

pip3 install назва_пакета

Для видалення пакету використовують команду:

pip3 uninstall назва_пакета

Для оновлення пакету використовують команду:

pip3 install --upgrade назва_пакета

7.5. Стандартна бібліотека Python

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

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

Python надає авторитетну документацію для модулів.

7.5.1. Обробка відсутніх ключів словника: функція setdefault()

Спроба отримати доступ до словника за допомогою неіснуючого ключа генерує виняток (помилку).

Функція setdefault() створює елемент словника з ключем, якщо заданий ключ у словнику відсутній.
periodic_table = {'Hydrogen': 1, 'Helium': 2} (1)
print(periodic_table) (2)
carbon = periodic_table.setdefault('Ferrum', 26) (3)
print(periodic_table) (4)
  1. Визначання словника.

  2. Виведення значення ключів і їх значень на екран.

  3. Використання функції setdefault(): якщо ключа ще немає в словнику (ключ 'Ferrum' у словнику відсутній), у словник буде доданий даний ключ з новим значенням 26.

  4. Виведення результату додавання пари ключ: значення.

У результаті виведення буде таким:

{'Hydrogen': 1, 'Helium': 2}
{'Hydrogen': 1, 'Helium': 2, 'Ferrum': 26}

Якщо ми намагаємося присвоїти інше значення за замовчуванням вже існуючому ключу

periodic_table = {'Hydrogen': 1, 'Helium': 2}
helium = periodic_table.setdefault('Helium', 101)
print(periodic_table)

буде повернуто оригінальне значення і ніщо не зміниться:

{'Hydrogen': 1, 'Helium': 2}

7.5.2. Підрахунок елементів: функція Counter()

Дуже часто при розв’язуванні задач необхідно порахувати кількість елементів послідовності. Якщо говорити про такі лічильники, то в стандартній бібліотеці Python є спеціальний засіб - функція Counter(), яка міститься у модулі collections:

from collections import Counter
birds = ['stork', 'sparrow', 'owl', 'woodpecker']
birds_counter = Counter(birds)
print(birds_counter)

Результат роботи такого лічильника:

Counter({'sparrow': 1, 'woodpecker': 1, 'owl': 1, 'stork': 1})

7.5.3. Впорядкування словника по ключу: функція OrderedDict()

Порядок ключів в словнику не можна передбачити. Розглянемо такий приклад:

regions = {
    'sands': 'Sahara',
    'mountains': 'Himalayas',
    'rivers': 'Yangtze',
}
for region in regions:
    print(region)

Результатом виведення будуть ключі, але не обов’язково в порядку, в якому вони були додані у словник:

sands
rivers
mountains
Функція OrderedDict(), яка міститься у модулі collections, запам’ятовує порядок, в якому додавалися ключі, і повертає їх в тому ж порядку.

Спробуємо використати OrderedDict з послідовності кортежів виду «ключ: значення»:

from collections import OrderedDict
regions = OrderedDict([
    ('mountains', 'Himalayas'),
    ('sands', 'Sahara'),
    ('rivers', 'Yangtze')
])
for region in regions:
    print(region)

В даному прикладі порядок ключів словника збережений при виведенні:

mountains
sands
rivers

7.5.4. Виведення на екран: функція pprint()

Щоб виводити інформацію на екран використовують функцію print(). У випадку, коли результати виведення важко прочитати, можна використовувати pretty printer (гарний принтер) - функцію pprint(), яка міститься у модулі pprint:

from collections import OrderedDict
from pprint import pprint
regions = OrderedDict([
    ('mountains', 'Himalayas'),
    ('sands', 'Sahara'),
    ('rivers', 'Yangtze')
])
print(regions)
pprint(regions)

Порівняти результати використання функції print() і гарного принтера можна побачити у такому виведенні:

OrderedDict([('mountains', 'Himalayas'), ('sands', 'Sahara'), ('rivers', 'Yangtze')])
{'mountains': 'Himalayas',
 'sands': 'Sahara',
 'rivers': 'Yangtze'}

7.6. Завдання

7.6.1. Контрольні запитання

  1. Що робить інструкція import?

  2. Яка різниця між модулем і пакетом?

  3. Якщо у вас є функція myfunc(), яка міститься у модулі addfunc, то як би ви її викликали після імпортування цього модуля?

  4. Для чого використовується модуль __main__?

7.6.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

  1. Створіть файл, який називається city.py. У ньому оголосіть функцію library_services(), яка виводить на екран рядок Internet access is open 24 hours.. Використайте інтерактивний інтерпретатор, щоб імпортувати модуль city.py і викликати його функцію library_services().

  2. У інтерактивному інтерпретаторі імпортуйте модуль city.py під ім’ям ct і викличте його функцію library_services().

  3. Залишаючись у інтерпретаторі, імпортуйте безпосередньо функцію library_services() з модуля city.py і викличте її.

  4. Імпортуйте функцію library_services() під ім’ям info і викличте її.

7.6.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

  1. Файл models.py містить програмний код, поданий нижче, що імітує друкування 3D-моделей різних об’єктів. Перенесіть функції print_models() і show_completed_models() у окремий файл з ім’ям printing_functions.py. Виконайте імпорт цих функцій у файл models.py, змінивши файл так, щоб у ньому імпортовані функції можна було використовувати.

def print_models(unprinted_designs, completed_models):
    """
        Імітує друк 3D-моделей, доки список не стане порожнім.
        Кожна модель після друку переміщується у completed_models.
    """
    while unprinted_designs:
        current_design = unprinted_designs.pop()
        # Імітація друку моделі на 3D-принтері.
        print("Printing model: " + current_design)
        completed_models.append(current_design)

def show_completed_models(completed_models):
    """Виводить інформацію про усі надруковані моделі."""
    print("\nThe following models have been printed:")
    for completed_model in completed_models:
        print(completed_model)
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = []
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
  1. Візьміть за основу одну з написаних вами програм з однією функцією. Збережіть цю функцію в окремому файлі. Імпортуйте функцію у файл основної програми і викличте функцію будь-яким із таких способів:

імпорт ім'я_модуля
from ім'я_модуля import ім'я_функції
from ім'я_модуля import ім'я_функції as псевдонім
import ім'я_модуля as псевдонім
from ім'я_модуля import *

8. Файли

Активна програма працює з даними, які зберігаються в оперативній пам’яті (Random Access Memory). RAM - дуже швидка пам’ять, але вимагає постійного живлення; якщо живлення зникає, то всі дані, які в ній зберігаються, будуть втрачені. Різноманітні накопичувачі (жорсткі диски, твердотільні диски, флеш-диски тощо) можуть зберігати дані навіть після того, як вимкнуть живлення.

Найпростіший приклад сховища даних - це звичайний файл. Дані зчитуються з файлу в пам’ять і записуються з пам’яті у файл.

Серед типів файлів можна виділити дві групи: прості текстові файли і бінарні файли.

Прості текстові файли - містять лише базові текстові символи, без інформації про шрифт, розмір і колір тексту тощо. Прикладом таких файлів є файли з розширенням .txt або файли Python з розширенням .py.
Бінарні (двійкові) файли - це файли документів, які створені текстовими процесорами, PDF-файли, файли зображень, елетронних таблиць і, взагалі, виконуваних програм.

Приклад бінарного файлу (файл інтерпретатора Python), відкритого у програмі Блокнот:

Вміст бінарного файлу, відкритого у Блокноті
Вміст бінарного файлу: файл відкритий у програмі Блокнот

8.1. Відкриття файлу

Перед тим як щось записати у файл або зчитати з нього, спочатку необхідно відкрити файл:

fileobj = open(filename, mode)
  • fileobj - об’єкт файлу, який повертається функцією open()

  • filename - рядок з назвою файлу

  • mode - рядок, який вказує на тип файлу і дії, які можна виконувати над файлом

Перша літера рядка mode вказує на операцію з файлом:

  • r - означає читання з файлу

  • w - означає запис в існуючий файл (якщо файла не існує, він буде створений)

  • x - означає запис у файл, але тільки якщо файлу не існує

  • a - означає додавання в кінець файлу, якщо файл існує

Друга літера рядка mode вказує на тип файлу:

  • t (або нічого) - означає, що файл текстовий

  • b - означає, що файл бінарний

Після відкриття файлу, виконують читання даних з файлу або запис даних у файл.

Потім необхідно файл закрити.

8.2. Запис даних у текстові файли

Використаємо як джерело даних фрагмент вірша Тараса Шевченка англійською мовою, що міститься у змінній poem і запишимо цей вірш у файл result:

poem = '''The mighty Dnieper roars and bellows,
The wind in anger howls and raves,
Down to the ground it bends the willows,
And mountain-high lifts up the waves.'''
frecord = open('result.txt', 'wt')
a = frecord.write(poem)
frecord.close()
print(a)

Функція write(), значення якої було присвоєне змінній a, повертає число записних у файл байтів

151

але не додає ніяких інших символів у файл, як це робить функція print(), яка також дозволяє записувати у файл:

frecord = open('result.txt', 'wt')
print(poem, file=frecord)
frecord.close()

Для запису у файл можна використовувати обидві функції. Варто лише пам’ятати про два аргументи функції print():

  • sep - розділювач, за замовчуванням це пропуск (' ')

  • end - символ кінця файлу, за замовчуванням це символ нового рядка (\n)

Для того щоб функція print() працювала як функція write(), змінений код виглядатиме так:

frecord = open('result.txt', 'wt')
print(poem, file=frecord, sep='', end='')
frecord.close()

Перевіримо, чи «врятує» режим x файл result.txt

frecord = open('result.txt', 'xt')

від його перезаписування?

Traceback (most recent call last):
  File "C:\Python34\test.py", line 1007, in <module>
    frecord = open('result.txt', 'xt')
FileExistsError: [Errno 17] File exists: 'result.txt'

Перезаписати не вдалося. Виникла помилка і згенерувався виняток. У такому випадку корисно використовувати обробник винятку:

try:
    frecord = open('result.txt', 'xt')
    frecord.write(poem)
except FileExistsError:
    print('result.txt already exists!')

Результатом використання обробника винятку буде повідомлення про те, що файл з таким ім’ям вже існує і записати у нього інформацію неможливо:

result.txt already exists!

8.3. Зчитування даних з текстових файлів

Щоб прочитати дані з файлу, можна використовувати такі функції: read(), readline(), readlines().

Щоб прочитати увесь вміст файлу за один раз, використаємо функцію read().

У випадку великих файлів і використання функції read(), для зчитування даних з файлів, потрібно більший обсяг оперативної пам’яті.
freading = open('result.txt', 'rt' )
poem = freading.read()
freading.close()
print(poem) (1)
print(len(poem)) (2)
  1. Виведення усього вмісту файлу за один раз.

  2. Виведення кількості байтів, зчитаних з файлу.

Результатом зчитування даних з файлу буде такий:

The mighty Dnieper roars and bellows,
The wind in anger howls and raves,
Down to the ground it bends the willows,
And mountain-high lifts up the waves.
151

Можна вказати максимальну кількість символів, які зчитуються з файлу за один раз. Давайте будемо зчитувати по 60 символів за раз і приєднувати кожний фрагмент до рядка poem, щоб відтворити увесь вміст файлу:

poem = ''
freading = open('result.txt', 'rt' )
chunk = 60
while True:
    fragment = freading.read(chunk)
    if not fragment:
        break
    poem += fragment
    print(len(fragment))
freading.close()

Після того як зчитали увесь файл, подальші виклики функції read() будуть повертати порожній рядок (' '). Це буде оцінено як False в перевірці if not fragment і дозволить вийти з нескінченного циклу while True.

Можна також зчитувати файл по одному рядку за раз за допомогою функції readline(). У наступному прикладі будемо приєднувати кожен рядок до рядка poem, щоб відтворити увесь вміст файлу:

poem = ''
freading = open('result.txt', 'rt' )
while True:
    line = freading.readline()
    if not line:
        break
    poem += line
freading.close()
print(poem)

Для текстового файлу навіть порожній рядок має довжину, яка дорівнює 1 (символ нового рядка \n), такий рядок буде вважатися True. Коли весь файл буде зчитаний, функція readline() (як і функція read()) поверне порожній рядок, яка буде вважатися False.

Найпростіший спосіб прочитати текстовий файл - використовувати цикл. Він буде повертати по одному рядку за раз. Цей приклад схожий на попередній, але коду у ньому менше:

poem = ''
freading = open('result.txt', 'rt' )
for line in freading:
    poem += line
freading.close()
print(poem)

У всіх попередніх прикладах в результаті повертався один рядок poem. Функція readlines() зчитує по одному рядку за раз і повертає список цих рядків:

freading = open('result.txt', 'rt' )
lines = freading.readlines()
freading.close()
print(len(lines), 'lines read')
for line in lines:
    print(line, end='')

У функції print() не було додано автоматично символи нового рядка \n, оскільки перші три рядки вже їх мають. В останньому рядку цього символу не було, що змусило інтерактивне запрошення >>> з’явитися відразу після останнього рядка.

4 lines read
The mighty Dnieper roars and bellows,
The wind in anger howls and raves,
Down to the ground it bends the willows,
And mountain-high lifts up the waves.>>>

8.4. Бінарні файли

Якщо включити символ 'b' в рядок mode, файл буде відкритий в бінарному режимі. В цьому випадку замість читання і запису рядків операції виконуються з байтами.

Щоб працювати з бінарними файлами, згенеруємо 256 байтових значень від 0 до 255 і відкриємо файл bfile у бінарному режимі для запису цих згенерованих даних:

bdata = bytes(range(0, 256))
frecord = open('bfile', 'wb')
frecord.write(bdata)
frecord.close()

Як і у випадку з текстом, бінарні дані можна записувати (зчитувати) фрагментами. Запишемо у файл згенеровані бінарні дані фрагментами не більшими за 100 байт:

bdata = bytes(range(0, 256))
frecord = open('bfile', 'wb')
size = len(bdata)
offset = 0
chunk = 100
while True:
    if offset > size:
        break
    result = frecord.write(bdata[offset:offset+chunk])
    print(result)
    offset += chunk
frecord.close()

Функція print(result) буде візуалізувати цей процес запису - на екран буде виводитися кількість вже записаних байтів:

100
100
56

Щоб прочитати бінарний файл - треба відкрити його у бінарному режимі rb:

freading = open('bfile', 'rb')
bdata = freading.read()
freading.close()

При виконанні операцій з бінарними файлами для відслідковування місцезнаходження у файлі конкретних байтів використовують функції tell() і seek().

Для прикладу скористаємося 256-байтним бінарним файлом 'bfile', який був створений раніше:

freading = open('bfile', 'rb')
print(freading.tell()) (1)
print(freading.seek(25)) (2)
  1. Функція tell() повертає ціле число - поточне розташування від початку файлу, виміряне у байтах.

  2. Функцію seek() використовують для того, щоб зміститися до конкретного байту у файлі.

0 # знаходимось на нульовому байті у файлі
25 # йдемо до 25-го байта у файлі

Ви також можете викликати функцію seek(), передавши їй другий аргумент:

seek(offset, origin)

або по аналогії

seek(скільки, звідки)
  • якщо значення origin дорівнює 0 (за замовчуванням), зміститися на offset байт з початку файлу

  • якщо значення origin дорівнює 1, зміститися на offset байт з поточної позиції праворуч

  • якщо значення origin дорівнює 2, зміститися на offset байт з кінця файлу ліворуч

Завдяки цьому, ми можемо перейти до будь-якого байту у файлі. Об’єднаємо усі прийоми, написавши програму з коментарями для кращого розуміння процесу:

freading = open('bfile', 'rb') # відкриваємо файл у бінарному режимі (містить 256 байтових значень)
freading.seek(251, 0) # переходимо у позицію за 5 байтів до кінця файлу
print(freading.tell()) # виводимо на екран поточну позицію, рахуючи від початку файлу
freading.seek(3, 1) # переходимо на 3 байти праворуч з поточної позиції
print(freading.tell()) # виводимо на екран поточну позицію, рахуючи від початку файлу
bdata = freading.read() # зчитуємо усі байти до кінця файлу (їх усього 2)
print(bdata) # виводимо на екран зчитані дані

Результатами виведення трьох функцій print() у тому ж порядку, як вони записані, будуть такі рядки:

251
254
b'\xfe\xff'
Функції tell() і seek() найбільш корисні при роботі з бінарними файлами. Їх можна використовувати і для роботи з текстовими файлами, проте, якщо файл містить не тільки символи формату ASCII (що займають по одному байту у пам’яті), буде важко визначити місцеположення символу, оскільки воно буде залежати від системи кодування тексту. Наприклад, кодування UTF-8 використовує різну кількість байтів для різних символів.

8.5. Закриття файлів автоматично з with

Файл повинен бути закритий після того, як усі операції з ним завершені.

У Python є менеджери контексту для очищення об’єктів (звільнення ресурсів), на зразок відкритих файлів. Менеджер контексту має таку конструкцію:

with вираз as змінна:

Наприклад:

with open('result.txt', 'wt') as freading:
	freading.write(poem)

Після того як блок коду, розташований у менеджері контексту (у цьому випадку один рядок freading.write(poem)), завершиться (або звичайним способом, або шляхом генерації винятку), файл буде закритий автоматично. Цією конструкцією варто користуватися у багатьох випадках.

8.6. Структуровані текстові файли

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

Існує ряд популярних форматів, які можна розрізнити за такими ознаками.

  • Розділювач у вигляді табуляції (\t), коми (,) або вертикальної риски (|). Це приклад формату зі значеннями, розділеними комами (CSV).

  • Символи < і >, які описують теги. Це приклади XML і HTML файлів.

  • Розділові знаки. Прикладом є JavaScript Object Notation (JSON).

8.6.1. CSV

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

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

import csv
programmers = [
    ['Python', 'Guido van Rossum'],
    ['Scala', 'Martin Odersky'],
    ['PHP', 'Rasmus Lerdorf'],
    ['Ruby', 'Yukihiro Matsumoto'],
    ['C', 'Dennis Ritchie'],
]
with open('programmers.csv', 'wt') as frecord: # менеджер контексту
    csvrecord = csv.writer(frecord)
    csvrecord.writerows(programmers)

Цей код створює файл з п’ятьма записами:

Python,Guido van Rossum

Scala,Martin Odersky

PHP,Rasmus Lerdorf

Ruby,Yukihiro Matsumoto

C,Dennis Ritchie

Тепер спробуємо зчитати ці записи:

import csv
with open('programmers.csv', 'rt') as freading: # менеджер контексту
    creading = csv.reader(freading)
    programmers = [row for row in creading] # тут використовується включення списку
print(programmers)

Виконавши попередній код, отримаємо список списків:

[['Python', 'Guido van Rossum'], [], ['Scala', 'Martin Odersky'], [], ['PHP', 'Rasmus Lerdorf'], [], ['Ruby', 'Yukihiro Matsumoto'], [], ['C', 'Dennis Ritchie'], []]
Використовуючи функції reader() і writer() із стандартними опціями, ми отримуємо колонки, які розділені комами, і рядки, розділені символами нового рядка.

Дані можуть мати формат списку словників, а не списку списків. Знову зчитаємо файл programmers.csv, цього разу використовуючи нову функцію DictReader() і вказуючи імена колонок:

import csv
with open('programmers.csv', 'rt') as freading:
    creading = csv.DictReader(freading, fieldnames=['language', 'developer'])
    programmers = [row for row in creading]
print(programmers)

Результатом буде список словників:

[{'developer': 'Guido van Rossum', 'language': 'Python'}, {'developer': 'Martin Odersky', 'language': 'Scala'}, {'developer': 'Rasmus Lerdorf', 'language': 'PHP'}, {'developer': 'Yukihiro Matsumoto', 'language': 'Ruby'}, {'developer': 'Dennis Ritchie', 'language': 'C'}]

Перепишемо CSV-файл з допомогою функції DictWriter(). Також зробимо виклик функції writeheader(), щоб записати початковий рядок, що містить імена колонок, у CSV-файл:

import csv
programmers = [
    {'language': 'Python', 'developer': 'Guido van Rossum'},
    {'language': 'Scala', 'developer': 'Martin Odersky'},
    {'language': 'PHP', 'developer': 'Rasmus Lerdorf'},
    {'language': 'Ruby', 'developer': 'Yukihiro Matsumoto'},
    {'language': 'C', 'developer': 'Dennis Ritchie'},
]
with open('programmers.csv', 'wt') as frecord:
    crecord = csv.DictWriter(frecord, ['language', 'developer'])
    crecord.writeheader()
    crecord.writerows(programmers)

Цей код створює файл programmers.csv з рядком заголовку:

language,developer

Python,Guido van Rossum

Scala,Martin Odersky

PHP,Rasmus Lerdorf

Ruby,Yukihiro Matsumoto

C,Dennis Ritchie

Тепер зчитаємо файл programmers.csv, не враховуючи аргумент fieldnames у виклику DictReader()

import csv
with open('programmers.csv', 'rt') as freading:
    creading = csv.DictReader(freading)
    programmers = [row for row in creading]
print(programmers)

використовуючи значення першого рядка файлу (language, developer) як імена колонок і відповідні ключі словника:

[{'language': 'Python', 'developer': 'Guido van Rossum'},
{'language': 'Scala', 'developer': 'Martin Odersky'},
{'language': 'PHP', 'developer': 'Rasmus Lerdorf'},
{'language': 'Ruby', 'developer': 'Yukihiro Matsumoto'},
{'language': 'C', 'developer': 'Dennis Ritchie'}]

8.6.2. XML

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

XML є найвідомішим форматом розмітки, який можна використовувати у даному випадку. Для структурування даних цей формат використовує теги, як показано у наступному прикладі (файл menu.xml):

<?xml version="1.0"?>
<menu>
    <hotel roomnumber="101-137">
        <item price="$42.60">single room</item>
        <item price="$24.45">restaurant</item>
    </hotel>
    <campsite roomnumber="201-265">
        <item price="$3.62">internet access</item>
    </campsite>
    <hostel roomnumber="301-312">
        <item price="$1.69">room service</item>
    </hostel>
</menu>
Деякі характеристики формату XML
  1. Теги починаються з символу <. У нашому прикладі використані теги menu, hotel, campsite, hostel і item.

  2. Пропуски ігноруються.

  3. Зазвичай, після початкового тега, для прикладу <menu>, слідує вміст, а потім відповідний кінцевий тег, на зразок, </menu>.

  4. Теги можуть бути вкладені у інші теги на будь-якому рівні. У нашому прикладі теги item є нащадками тегів hotel, campsite і hostel, які, у свою чергу, є нащадками тега menu.

  5. Всередині початкового тега можуть зустрітися опціональні атрибути. У нашому прикладі price є опціональним атрибутом тега item.

  6. Теги можуть містити значення. У цьому прикладі кожен тег item має значення, на зразок, internet access для елемента item тега campsite.

Найпростіший спосіб проаналізувати XML у Python - використати бібліотеку ElementTree. Розглянемо невелику програму, яка аналізує файл menu.xml і виводить на екран деякі теги і атрибути:

import xml.etree.ElementTree as et
tree = et.ElementTree(file='menu.xml')
root = tree.getroot()
print(root.tag)

for child in root:
    print('tag:', child.tag, 'attributes:', child.attrib)
    for grandchild in child:
        print('\ttag:', grandchild.tag, 'attributes:', grandchild.attrib)

print(len(root)) # кількість розділів menu
print(len(root[0])) # кількість елементів hotel

Для кожного елемента вкладених списків tag - це рядок тега, а attrib - це словник його атрибутів:

menu
tag: hotel attributes: {'roomnumber': '101-137'}
	tag: item attributes: {'price': '$42.60'}
	tag: item attributes: {'price': '$24.45'}
tag: campsite attributes: {'roomnumber': '201-265'}
	tag: item attributes: {'price': '$3.62'}
tag: hostel attributes: {'roomnumber': '301-312'}
	tag: item attributes: {'price': '$1.69'}
3
2
Бібліотека ElementTree має багато інших способів пошуку даних, організованих у форматі XML, модифікації цих даних і навіть запис XML-файлів. Всі деталі викладені у документації бібліотеки ElementTree.

8.6.3. JSON

JSON (JavaScript Object Notation) став дуже популярним форматом обміну даними. Він є частиною мови JavaScript, але, водночас, є гарним вибором при визначенні формату даних для обміну між програмами. Дані у цьому форматі можна передавати програмам, написаним на багатьох інших мовах програмування.

JSON широко використовується у веб-розробці.

Для роботи з цим форматом у Python існує модуль з ім’ям json.

Розглянемо програму, яка кодує (перетворює) дані у рядок JSON і декодує рядок JSON у протилежному випадку. У наступному прикладі створимо структуру даних, що містить дані з прикладу, де описувався формат XML:

menu = {
    "hotel": {
            "roomnumber": "101-137",
            "items": {
                    "single room": "$42.60",
                    "restaurant": "$24.45"
            }
    },
    "campsite" : {
        "roomnumber": "201-265",
        "items": {
            "internet access": "$3.62"
        }
    },
    "hostel": {
        "roomnumber": "301-312",
        "items": {
            "room service": "$1.69"
        }
    }
}

Закодуємо структуру даних (menu) в рядок JSON (menu_json) за допомогою функції dumps():

import json
menu_json = json.dumps(menu)
print(menu_json)

Результат кодування даних у формат JSON:

{"hostel": {"roomnumber": "301-312", "items": {"room service": "$1.69"}}, "hotel": {"roomnumber": "101-137", "items": {"restaurant": "$24.45", "single room": "$42.60"}}, "campsite": {"roomnumber": "201-265", "items": {"internet access": "$3.62"}}}

А тепер перетворимо рядок JSON menu_json назад у структуру даних (menu2) за допомогою функції loads():

import json
menu_json = json.dumps(menu)
menu2 = json.loads(menu_json)
print(menu2)

Отримаємо наступні результати:

{'hotel': {'roomnumber': '101-137', 'items': {'single room': '$42.60', 'restaurant': '$24.45'}}, 'hostel': {'roomnumber': '301-312', 'items': {'room service': '$1.69'}}, 'campsite': {'roomnumber': '201-265', 'items': {'internet access': '$3.62'}}}

menu і menu2 є словниками з однаковими за змістом ключами і значеннями.

8.7. Бази даних

Базами даних у комп’ютерному світі користуються повсюдно.

Cловосполучення «база даних» використовується у декількох випадках: коли мова йде про сервер, про сховище і про дані, які там зберігаються. Якщо потрібно згадати їх одночасно, можна назвати їх сервером бази даних, базою даних і даними.

Розглянемо реляцйні бази даних.

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

  • Діє захист від пошкодження даних користувачами.

  • Існують ефективні методи збереження і зчитування даних.

  • Об’єднання дозволяють знайти відношення між різними типами даних.

  • Декларативна (на противагу імперативної) мова запитів SQL (Structured Query Language, структурована мова запитів).

  • Таблиця являє собою сітку із записами (рядками) і полями (стовпцями), схожу на електронну таблицю.

  • Щоб створити таблицю, необхідно вказати її ім’я, імена і типи її полів. Кожен рядок має однакові поля.

  • Первинним ключем таблиці є поле або група полів, значення яких повинні бути унікальними. Це запобігає введення однакових даних в таблицю.

  • Первинний ключ індексується для більш швидкого пошуку під час виконання запитів. Робота індексу трохи схожа на алфавітний покажчик, що дозволяє швидко знайти певний запис (рядок).

  • Кожна таблиця знаходиться всередині батьківської бази даних, що нагадує файли в каталозі.

8.7.1. API

Програмний інтерфейс програми (Application Programming Interface, API) - це набір функцій, які ви можете викликати, щоб отримати доступ до будь-якої послуги.

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

Розглянемо його основні функції.

  • connect() - створення з’єднання з базою даних. Виклик цієї функції може включати в себе аргументи, на зраок, імені користувача, пароля, адреси сервера та ін.

  • cursor() - створення об’єкта курсору, призначеного для роботи із запитами.

  • execute() і executemany() - запуск однієї або більше команд SQL.

  • fetchone(), fetchmany() і fetchall() - отримання результатів роботи функції execute().

8.7.2. Мова запитів SQL

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

Існують дві основні категорії SQL:

  1. DDL (Data Definition Language, мова визначення даних) - обробляє створення, видалення, обмеження і дозволи для таблиць, баз даних.

  2. DML (Data Manipulation Language, мова маніпулювання даними) - обробляє додавання даних, їх вибірку, оновлення та видалення.

У таблиці, поданій нижче, перераховані основні команди SQL DDL:

Таблиця "Основні команди SQL DDL"
Операція Шаблон SQL Приклад SQL

Створення бази даних

CREATE DATABASE назва_бази_даних

CREATE DATABASE dbname

Вибір поточної бази даних

USE назва_бази_даних

USE dbname

Видалення бази даних і її таблиць

DROP DATABASE назва_бази_даних

DROP DATABASE dbname

Створення таблиці

CREATE TABLE назва_таблиці (назви полів та їхніх типів)

CREATE TABLE tbname (id INT, count INT)

Видалення таблиці

DROP TABLE назва_таблиці

DROP TABLE tbname

Видалення усіх записів (рядків) таблиці

TRUNCATE TABLE назва_таблиці

TRUNCATE TABLE tbname

Мова SQL не залежить від регістру, але ключові слова пишуться ВЕЛИКИМИ ЛІТЕРАМИ, щоб можна було відрізнити їх від назв полів.

Основні операції DML реляційної бази даних можна запам’ятати за допомогою акроніма CRUD:

  • Create - створення за допомогою оператора INSERT

  • Read - читання за допомогою SELECT

  • Update - оновлення за допомогою UPDATE

  • Delete - видалення за допомогою DELETE

У таблиці, поданій нижче, перераховані основні команди SQL DML:

Таблиця "Основні команди SQL DML"
Операція Шаблон SQL Приклад SQL

Додавання запису (рядка) у таблицю

INSERT INTO назва_таблиці VALUES(значення)

INSERT INTO tbname VALUES(7, 40)

Вибір усіх записів (рядків) і полів (стовпців) з таблиці

SELECT * FROM назва_таблиці

SELECT * FROM tbname

Вибір всіх записів (рядків) і деяких полів (стовпців) з таблиці

SELECT назва_поля1, назва_поля2 FROM назва_таблиці

SELECT id, count FROM tbname

Вибір деяких записів (рядків) і деяких полів (стовпців) з таблиці

SELECT назва_поля1, назва_поля2 FROM назва_таблиці WHERE назва_поля2 > значення2 AND назва_поля1 = значення1

SELECT id, count FROM tbname WHERE count > 5 AND id = 9

Зміна деяких записів (рядків) у полі (стовпці) таблиці

UPDATE назва_таблиці SET назва_поля1 = значення1 WHERE назва_поля2 = значення2

UPDATE tbname SET count = 3 WHERE id = 5

Видалення деяких записів (рядків) таблиці

DELETE FROM назва_таблиці WHERE назва_поля1 <= значення1 OR назва_поля2 = значення2

DELETE FROM tbname WHERE count <= 10 OR id = 16

8.7.3. SQLite

SQLite - це легка реляційна база даних з відкритим вихідним кодом.

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

На відміну від MySQL і PostgreSQL, SQLite має менше можливостей, але вона підтримує мову запитів SQL і дозволяє кільком користувачам працювати з нею одночасно.

Браузери, операційні системи використовують SQLite як вбудовану базу даних.

Робота з базою даних починається з виклику connect() для встановлення з’єднання з локальним файлом бази даних, який треба створити або використовувати.

Для прикладу, створимо базу даних ishop.db і таблицю сomputers, яка буде містити інформацію про товари інтернет-магазину комп’ютерної техніки. У таблиці будуть міститися такі поля:

  • id - унікальний номер товару (первинний ключ)

  • name - назва товару (рядок змінної довжини)

  • count - кількість одиниць конкретного товару (ціле число)

  • price - ціна одного екземпляру конкретного товару (дійсне число)

import sqlite3
conn = sqlite3.connect('ishop.db')
curs = conn.cursor()
curs.execute('''CREATE TABLE сomputers (id INT PRIMARY KEY, name VARCHAR(20), count INT, price FLOAT)''')
conn.commit()
Використовуйте потрійні лапки (''' ''') при створенні довгих рядків і при створенні запитів SQL.
Якщо записати для поля id тип INTEGER PRIMARY KEY, значення id буде збільшуватися на одиницю автоматично.

Рядок conn.commit() дозволяє зберети поточні зміни. Перед тим, як завершити роботу з SQLite, необхідно закрити курсор і з’єднання:

...
curs.close()
conn.close()

Тепер додамо у наш магазин декілька товарів:

import sqlite3
conn = sqlite3.connect('ishop.db')
curs = conn.cursor()
curs.execute('INSERT INTO сomputers VALUES(1, "PC", 5, 7570.50)')
curs.execute('INSERT INTO сomputers VALUES(2, "Notebook", 8, 11430.30)')
conn.commit()
curs.close()
conn.close()

Існує більш безпечний спосіб додавання даних - використовуючи заповнювач у вигляді ?:

import sqlite3
conn = sqlite3.connect('ishop.db')
curs = conn.cursor()
ins = 'INSERT INTO сomputers (id, name, count, price) VALUES(?, ?, ?, ?)'
curs.execute(ins, (3, 'TabletPC', 4, 3970.20))
ins = 'INSERT INTO сomputers (id, name, count, price) VALUES(?, ?, ?, ?)'
curs.execute(ins, (4, 'Console', 2, 16780.90))
conn.commit()
curs.close()
conn.close()

Цього разу у запиті використовувалися чотири знаки ?, щоб показати, що планується додати чотири значення, а потім додати ці значення списком у функцію execute().

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

Тепер перевіримо, чи зможемо ми отримати список наших товарів:

import sqlite3
conn = sqlite3.connect('ishop.db')
curs = conn.cursor()
curs.execute('SELECT * FROM сomputers')
conn.commit()
rows = curs.fetchall()
print(rows)
curs.close()
conn.close()

Результат виконання:

[(1, 'PC', 5, 7570.5), (2, 'Notebook', 8, 11430.3), (3, 'TabletPC', 4, 3970.2), (4, 'Console', 2, 16780.9)]

Отримаємо список знову, але на цей раз впорядкуємо його за кількістю товарів:

import sqlite3
conn = sqlite3.connect('ishop.db')
curs = conn.cursor()
curs.execute('SELECT * FROM сomputers ORDER BY count')
rows = curs.fetchall()
print(rows)
curs.close()
conn.close()

Результат впорядкування за зростанням за полем кількість:

[(4, 'Console', 2, 16780.9), (3, 'TabletPC', 4, 3970.2), (1, 'PC', 5, 7570.5), (2, 'Notebook', 8, 11430.3)]

Отримаємо даний список у протилежному порядку (за спаданням):

import sqlite3
conn = sqlite3.connect('ishop.db')
curs = conn.cursor()
curs.execute('SELECT * FROM сomputers ORDER BY count DESC')
rows = curs.fetchall()
print(rows)
curs.close()
conn.close()

Результат впорядкування за спаданням:

[(2, 'Notebook', 8, 11430.3), (1, 'PC', 5, 7570.5), (3, 'TabletPC', 4, 3970.2), (4, 'Console', 2, 16780.9)]

Зробимо вибірку товарів, які коштують найдорожче, використавши такий код:

import sqlite3
conn = sqlite3.connect('ishop.db')
curs = conn.cursor()
curs.execute('SELECT * FROM сomputers WHERE price = (SELECT MAX(price) FROM сomputers)')
rows = curs.fetchall()
print(rows)
curs.close()
conn.close()

Результат у даному випадку буде такий:

[(4, 'Console', 2, 16780.9)]

Оновимо дані у базі даних ishop.db. Змінимо кількість TabletPC з 4 на 7:

import sqlite3
conn = sqlite3.connect('ishop.db')
curs = conn.cursor()
curs.execute('UPDATE сomputers SET count = 7 WHERE id = 3')
conn.commit()
curs.execute('SELECT * FROM сomputers WHERE id = 3')
row = curs.fetchone()
print(row)
curs.close()
conn.close()

Запис у базі даних із значенням id = 3 зміниться на такий:

(3, 'TabletPC', 7, 3970.2)

Виконаємо операцію видалення товарів з бази даних за назвою PC

import sqlite3
conn = sqlite3.connect('ishop.db')
curs = conn.cursor()
curs.execute("DELETE FROM сomputers WHERE name = 'PC'")
conn.commit()
curs.execute("SELECT * FROM сomputers")
row = curs.fetchall()
print(row)
curs.close()
conn.close()

і відобразимо усі записи із бази даних (товар з назвою PC відсутній):

[(2, 'Notebook', 8, 11430.3), (3, 'TabletPC', 7, 3970.2), (4, 'Console', 2, 16780.9)]

8.8. Завдання

8.8.1. Контрольні запитання

  1. Які аргументи задають режим відкривання файлу з допомогою функції open()?

  2. За допомогою яких функцій можна записувати у файл?

  3. Чим відрізняється робота функцій read(), readline() і readlines()?

  4. Для чого використовують у роботі з файлами оператор with?

  5. Як створити і прочитати бінарний файл?

  6. Які засоби Python використовуються для відслідковування місцезнаходження конкретних байтів у бінарних файлах?

  7. Як використовуються функції csv.reader() і csv.writer?

  8. Яка функція приймає JSON-рядок і повертає структуру даних Python?

  9. Яка функція приймає структуру даних Python, а повертає рядок JSON-даних?

  10. Які інструменти Python використовують для отримання доступу до баз даних і роботи з ними?

  11. Що таке SQL?

  12. Наведіть приклад запиту з використанням мови SQL.

8.8.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

  1. Збережіть рядок 'Test variable to write to file' у змінну test1 і запишіть змінну test1 у файл з ім’ям tests.txt.

  2. Відкрийте файл tests.txt і зчитайте його вміст у рядок test2.

  3. Порівняйте рядки test1 і test2?

  4. Скопіюйте наступні кілька рядків у файл painters.csv.

author, canvas
Vincent Willem van Gogh, "Vase with sunflowers"
Rembrandt Harmenszoon van Rijn, "Aristotle"
Leonardo da Vinci, "Self-portrait"
  1. Використайте модуль csv і його метод DictReader, щоб зчитати вміст файлу painters.csv у змінну painters і виведіть її значення на екран.

  1. Створіть CSV-файл imdb.csv з рейтингом фільмів за версією IMDb, використовуючи список словників:

imdb = [
    {
        'title': 'Lord of the Rings: Two towers',
        'year': 2002,
        'rating': 8.7},
    {
        'title': 'Matrix',
        'year': 1999,
        'rating': 8.7},
    {
        'title': 'Interstellar',
        'year': 2014,
        'rating': 8.5},
    {
        'title': 'Back to the Future',
        'year': 1985,
        'rating': 8.5},
    {
        'title': 'Logan: Wolverine',
        'year': 2017,
        'rating': 8.1}
]

8.8.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

  1. Створіть новий файл numbers.txt у текстовому редакторі і запишіть у нього 10 чисел, кожне з нового рядка. Напишіть програму, яка зчитує ці числа з файлу і обчислює їх суму, виводить цю суму на екран і, водночас, записує цю суму у інший файл під назвою sum_numbers.txt.

  2. Реалізуйте програму, яка зчитує ціле число, що вводиться з командного рядка, і записує у текстовий файл інформацію, щодо парності або непарності числа.

  3. Створіть новий файл у текстовому редакторі і напишіть кілька рядків тексту у ньому про можливості Python. Кожен рядок повинен починатися з фрази: In Python you can .... Збережіть файл під ім’ям learning_python.txt. Напишіть програму, яка зчитує файл і виводить текст з перебором рядків об’єкта файлу і зі збереженням рядків у списку з подальшим виведенням списку поза блоком with.

  4. Функція replace() може використовуватися для заміни будь-якого слова у рядку іншим словом. Прочитайте кожен рядок зі створеного у попередньому завданні файлу learning_python.txt і замініть слово Python назвою іншої мови, наприклад C при виведенні на екран.

  5. Створіть порожній файл guest_book.txt у текстовому редакторі. Напишіть цикл while, який запитує у користувачів імена. При введенні кожного імені виведіть на екран рядок з вітанням для користувача і запишіть рядок вітання у файл з ім’ям guest_book.txt. Простежте за тим, щоб кожне повідомлення розміщувалося в окремому рядку файлу. Передбачте вихід з циклу.

  6. Зайдіть на сайт Project Gutenberg’s і знайдіть кілька книг для аналізу. Завантажте текстові файли цих творів або скопіюйте текст з браузера у текстовий файл на вашому комп’ютері. Напишіть програму, яка зчитує дані з файлу і визначає кількість входжень слова 'the' в кожному тексті незалежно від регістру. Для підрахунку кількості входжень слова або виразу в рядок можна скористатися методом count().

  7. Завантажте текстову версію однієї з книг із сайту Project Gutenberg’s. Замініть усі розриви рядків у тексті символом пропуску і запишіть відформатований текст у новий файл formatted_text.txt.

  8. Завантажте текстову версію книги The Life and Adventures of Robinson Crusoe By Daniel Defoe із сайту Project Gutenberg’s. Витягніть із тексту заголовки усіх розділів, які мають вигляд, на зразок: CHAPTER I—START IN LIFE. Запишіть знайдені назви у новий файл chapters.txt.

  9. Визначте відсоток малих і великих літер у тексті, що зберігається у файлі. Скористайтеся, як зразком вхідного файлу, текстовий файл із сайту Project Gutenberg’s. Використайте функцію isalpha().

  10. Використайте модуль sqlite3, щоб створити базу даних SQLite під назвою imdb.db і таблицю ratings, що містить наступні поля: id (INTEGER PRIMARY KEY), title (VARCHAR(20)), year (INT), rating (FLOAT). Зчитайте дані з файлу imdb.csv і додайте їх у таблицю ratings. Зчитайте і виведіть на екран усі значення таблиці ratings у алфавітному порядку за полем title . Зчитайте і виведіть на екран усі записи таблиці ratings з ретингом 8.70.

9. Система

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

Python надає багато системних функцій, що містяться в модулі os (скорочення від Operating System - операційна система), який необхідно імпортувати для роботи програм.

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

9.1. Файли і папки

Файл - це послідовність байтів, яка зберігається під певною назвою.

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

Наприклад, файл з назвою projects.docs має тип файлу - документ Word, про що свідчить розширення docs. Частина назви файлу (projects) до останньої крапки - цей ім’я файлу, яке вказує користувач.

Файл має дві ключові ознаки: назва файлу і шлях до файлу.

Шлях визначає, де саме на комп’ютері знаходиться файл. Наприклад, файл з назвою projects.docs може мати шлях до файлу C:\Users\gt\Documents. Це означає, що файл projects.docs зберігається у папці Documents.

Частина C:\ шляху до файлу - це коренева папка, яка містить усі інші папки (gt, Users) і сам файл projects.docs.

У системах Windows кореневою папкою є C:\, а шляхи до файлів записують з використанням зворотного слешу (\), який розділяє імена папок. В Mac OS X і Linux кореневою папкою є папка /, а розділення імен папок виконують з використанням прямого слеша (/).

Організацію папок у Windows показує наступна ілюстрація:

Розміщення файлів у ієрархії папок у Windows
Спосіб організації папок: розміщення файлів у ієрархії папок у Windows

Шлях до файлу (або папки), який враховує використання обох слешів, можна сформувати з використанням функції join(), яка міститься у модулі path, який у свою чергу міститься у модулі os.

Інформацію про використання модуля os.path можна знайти на сайті документації Python.

Якщо передати у цю функцію значення назв файлів і папок і виконати в середовищі Windows

import os
print(os.path.join('usr', 'bin', 'media'))

функція поверне значення, у якому сформується шлях з коректною підстановкою слеша-розділювача:

usr\bin\media

Якщо ж виконати даний приклад в інтерактивній оболонці Python у Windows, виклик функції os.path.join() поверне рядок:

'usr\\bin\\media'
Cимволи зворотного слешу продубльовані, оскільки кожний з них потребує екранування іншим символом зворотного слеша.

Якщо викликати вищезгадану фукнцію os.path.join() в Mac OS X або Linux, вона поверне рядок:

'usr/bin/media'

Для формування шляхів до файлів або папок у вигляді рядків використовується функція os.path.join(). Наприклад, наступний код

import os
myFiles = ['accounts.txt', 'details.csv', 'invite.docs']
for filename in myFiles:
    print(os.path.join('C:\\Users\\gt\\Documents', filename))

формує шляхи до файлів, додаючи до імен папок назви файлів зі списку:

C:\Users\gt\Documents\accounts.txt
C:\Users\gt\Documents\details.csv
C:\Users\gt\Documents\invite.docs

Кожна програма, яка виконується на комп’ютері, має свій поточний робочий каталог (current working directory - cwd).

Будь-які шляхи до файлів або папок, які не починаються з кореневої папки, задаються відносно поточного робочого каталога.

Для отримання значення поточного робочого каталога у вигляді рядка використовується функція os.getcwd(), а для переходу з поточного робочого каталогу у інший каталог (папку) - функція os.chdir().

import os
print(os.getcwd())
os.chdir('C:\\Windows\\Temp')
print(os.getcwd())

У даному випадку, в якості робочого поточного каталогу встановлюється папка по шляху C:\Python34. Результатом переходу між папками є наступні виведенні рядки:

C:\Python34
C:\Windows\Temp
При спробі перейти у папку, якої не існує, Python виведе повідомлення про помилку.

Існує два способи визначення шляху до файла або папки:

  • абсолютний шлях - завжди починається з імені кореневої папки

  • відносний шлях - задається відносно поточного робочого каталогу програми

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

Зрозумілою мовою, позначення . означає «поточна папка», а позначення .. - «папка вищого рівня».

Приклад розташування папок і файлів показаний на даній ілюстрації (в якості поточного робочого каталогу обраний C:/books):

Відносні і абсолютні шляхи у Windows
Доступ до файлів і папок у Windows: відносні і абсолютні шляхи доступу до папок і файлів
Використовування імені .\ на початку відносного шляху є необов’язковим. Наприклад, шляхи .\authors.txt і authors.txt ведуть до одного файлу.

Для створення нових каталогів (папок) використовується функція os.makedirs(), яка створює усі необхідні проміжні папки:

os.makedirs('C:\\Python34\\newfolder\my')

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

  • Виклик os.path.abspath(path) - повертає рядок абсолютного шляху до файлу або папки.

  • Виклик os.path.isabs(path) - повертає True, якщо шлях є абсолютним, і False у протилежному випадку, коли шлях виявиться відносним.

  • Виклик os.path.relpath(path, start) - повертає рядок відносного шляху від точки start до path. Якщо start не вказаний, то в якості нього використовується поточний робочий каталог.

На наступному прикладі коду:

print(os.path.abspath('.'))
print(os.path.abspath('.\\Scripts'))
print(os.path.isabs('.'))
print(os.path.isabs(os.path.abspath('.')))
print(os.path.relpath('C:\\Windows', 'C:\\'))
print(os.path.relpath('C:\\Windows', 'C:\\Python34\\Scripts'))
print(os.getcwd())

робота цих функцій виглядає так:

C:\Python34
C:\Python34\Scripts
False
True
Windows
..\..\Windows
C:\Python34

Для отримання із повного шляху лише назви файлу використовують функцію os.path.basename(). Якщо необхідно отримати частину шляху без назви файлу використовують функцію os.path.dirname().

Якщо шлях до файлу вважати таким рядком 'C:\\Windows\\System32\\calc.exe', то ці дві функції:

path = 'C:\\Windows\\System32\\calc.exe'
print(os.path.basename(path))
print(os.path.dirname(path))

відпрацюють так:

calc.exe
C:\Windows\System32

Щоб отримати одночасно і назву файла, так і імена папок на шляху до нього, можна скористатися функцією split() у такому виконанні:

path = 'C:\\Windows\\System32\\calc.exe'
print(os.path.split(path))

Результатом виведення буде кортеж з двох елементів:

('C:\\Windows\\System32', 'calc.exe')

Щоб отримати список усіх папок на шляху до файлу і назву самого файлу використовують розділювач os.sep.

path = 'C:\\Windows\\System32\\calc.exe'
print(path.split(os.path.sep))

Для системи Windows буде такий результат:

['C:', 'Windows', 'System32', 'calc.exe']

Для систем Mac OS X і Linux

print('/usr/bin'.split(os.path.sep))

першим елементом такого списку буде порожній рядок:

['', 'usr', 'bin']

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

  • os.path.getsize(path) - повертає у байтах розмір файлу, шлях до якого вказаний у path.

  • os.listdir(path) - повертає список рядків з назвами усіх файлів, шлях до яких вказаний у path.

Функція listdir() міститься у модулі os, а не у модулі os.path.

Коли виконати поданий код

print(os.path.getsize('C:\\Windows\\System32\\calc.exe'))
print(os.listdir('C:\\Windows\\System32'))

отримаємо результати роботи розглянутих функцій:

26112
['0409', '12520437.cpx', '12520850.cpx', ...
...
... zipfldr.dll', 'ztrace_maps.dll']

Для знаходження сумарного розміру файлів, наприклад, у папці C:\\Python34, можна використати таку програму:

total = 0
for filename in os.listdir('C:\\Python34'):
    total = total + os.path.getsize(os.path.join('C:\\Python34', filename))
print(total)

Результатом виконання даної програми буде розмір у байтах усіх файлів папки C:\\Python34:

2179489

Функції Python для роботи з файлами і папками можуть повідомляти про помилки, коли не знаходять шлях до файлу або папки. Модуль os.path містить функції, які перевіряють, чи існує шлях до папки або файлу:

  • os.path.exists(path) - повертає значення True, якщо файл (або папка) за вказаним шляхом path існує, і значення False у протилежному порядку

  • os.path.isfile(path) - повертає значення True, якщо файл за вказаним шляхом path існує, і значення False у протилежному порядку

  • os.path.isdir(path) - повертає значення True, якщо папка за вказаним шляхом path існує, і значення False у протилежному порядку

Спробуємо подані функції у дії:

print(os.path.exists('C:\\Windows'))
print(os.path.exists('C:\\some_made_up_folder'))
print(os.path.isdir('C:\\Windows\\System32'))
print(os.path.isfile('C:\\Windows\\System32'))
print(os.path.isdir('C:\\Windows\\System32\\calc.exe'))
print(os.path.isfile('C:\\Windows\\System32\\calc.exe'))

Результат перевірки матимуть такий вигляд:

True
False
True
False
False
True

Щоб дізнатися, чи існують файли або каталоги з певними назвами, використовують функцію glob(), яка працює за певними правилами, які мають вигляд:

  • * - збігається з усім

  • ? - збігається з одним символом

  • [Abc] - збігається з символами a, b або c

  • [! Abc] - збігається з усіма символами, окрім a, b або c

Отримаємо всі файли і каталоги, імена яких:

  • починаються з літери o

  • складаються з двох символів

  • містять слово, яке починається з c, далі містить довільну кількість символів і закінчується на db

  • починаються з літер k, або m, або r

import glob
print(glob.glob('o*'))
print(glob.glob('??'))
print(glob.glob('c*db'))
print(glob.glob('[kmr]*'))

Результат пошуку файлів або папок може бути наступним:

['open_port.py', 'open_port_s.py']
[]
['cities.db']
['keywords.txt', 'monitors.pdf', 'README.txt']

Для виконання операції копіювання використовується функція copy(), яка знаходиться у модулі shutil. У наступному прикладі вміст файлу file1.txt копіюється у файл file2.txt:

import shutil
shutil.copy('file1.txt', 'file2.txt')
Функція shutil.move() копіює файл, а потім видаляє оригінал.

Для перейменування імен файлів використовують функцію rename(). Роботу функції rename() можна побачити у наступному прикладі: файл one.txt перейменовується в two.txt:

import os
os.rename('one.txt', 'two.txt')

Щоб видалити файл, використовується функція remove(), а для видалення папок - функція os.rmdir. Щоб «попрощатися» з файлом file.txt і папкою newfolder, виконайте такі команди:

import os
os.remove('file.txt')
os.rmdir('newfolder')

9.2. Дата й час

Процес роботи з датами й часом викликає багато проблем, оскільки дати можуть бути представлені різними способами. Ось деякі з варіантів представлення простих дат:

  • July 29 1981

  • 29 Jul 1981

  • 29/7/1981

  • 7/29/1981

Cистемні значення цієї дати можуть бути неоднозначними. У деяких з цих варіантів досить легко визначити, що 7 означає місяць, а 29 - день місяця, в основному тому що у місяці не може бути номера 29.

Але як щодо дати 1/7/2017?

Високосні роки - це ще одна проблема. Кожен четвертий рік є високосним, а кожен сотий рік не є високосним, а кожен 400-й - є.

Розглянемо приклад коду,

import calendar
print(calendar.isleap(1900))
print(calendar.isleap(2000))
print(calendar.isleap(2017))

в якому перевіряється, чи є рік високосним:

False
True
False

Робота з часом також може завдати неприємностей, особливо із-за годинних поясів і переходу на літній час. Якщо поглянути на карту годинних поясів, то виявиться, що ці пояси більше відповідають політичним та історичним кордонам, замість того, щоб змінюватися кожні 15 градусів (360 / 24) довготи. Крім того, різні країни переходять на літній час і назад у різні дні року. Фактично, країни південної півкулі переводять свої годинники вперед, коли країни північної півкулі переводять їх назад, і навпаки.

Стандартна бібліотека Python має багато модулів для роботи з датою і часом: datetime, time, calendar та ін.

9.2.1. Модуль datetime

У цьому модулі визначено такі об’єкти:

  • date для років, місяців і днів

  • time для годин, хвилин, секунд і часток секунди

  • datetime для дати і часу одночасно

  • timedelta для інтервалів дати і/або часу

Ви можете створити об’єкт date, вказавши рік, місяць і день.

import datetime
from datetime import date
independence_day = date(2017, 7, 24)
print(independence_day)
datetime.date(2017, 7, 24)
print(independence_day.day)
print(independence_day.month)
print(independence_day.year)
print(independence_day.isoformat())

Ці значення (рік, місяць і день) будуть доступні як атрибути:

2017-07-24
24
7
2017
2017-07-24

Останній рядок у коді виводить вміст об’єкту date з використанням функції isoformat(). iso у даному контексті відноситься до ISO 8601.

ISO 8601 - міжнародний стандарт для представлення дати й часу.

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

Згенеруємо сьогоднішню дату, використовуючи функцію today():

from datetime import date
now = date.today()
print(now)

Результат може бути таким:

2017-07-07

У наступному прикладі

from datetime import date
from datetime import timedelta
now = date.today()
one_day = timedelta(days=1)
tomorrow = now + one_day
print(tomorrow)
tomorrow = now + 17*one_day
print(tomorrow)
yesterday = now - one_day
print(yesterday)

об’єкт timedelta використовується для того, щоб додати до об’єкта date певний часовий інтервал:

2017-07-08
2017-07-24
2017-07-06

Об’єкт datetime має функцію now(),

from datetime import datetime
now = datetime.now()
print(now) # дата й час повністю
print(now.month) # місяць
print(now.day) # день
print(now.hour) # години
print(now.minute) # хвилини
print(now.second) # секунди
print(now.microsecond) # частки секунди

за допомогою якої можна отримати поточні дату й час:

2017-07-07 19:06:00.197694
7
7
19
6
0
197694

9.2.2. Модуль time

Одним із способів подання абсолютного часу є підрахунок кількості секунд, що пройшли з деякої стартової точки. Наприклад, у Unix використовується кількість секунд, що пройшли з півночі 1 січня 1970. Це значення часто називають epoch («епоха Unix»), і часто воно є найпростішим способом обмінюватися датою і часом між системами.

Функція time() модуля time повертає поточний час як значення epoch, а функція ctime() конвертує значення epoch у рядок:

import time
now = time.time()
print(now)
print(time.ctime(now))

З результату видно, скільки пройшло секунд після настання 1970 року:

1500029436.132774
Fri Jul 14 13:50:36 2017
Значення epoch корисні для обміну датою і часом між різними системами.

Функцію time() модуля time можна використовувати для визначення періодів часу виконання блоків коду програми. Напишемо невелику програму, яка виводить числа від 1000 до 1 включно і повідомлення про час роботи програми:

import time
startTime = time.time()
for i in range(1000, 0, -1):
    print(i)
endTime = time.time()
print('The program has been running:', str(endTime - startTime), ' seconds.')

Якщо виконати подану програму, можна отримати подібний результат:

1000
999
998
...
3
2
1
The program has been running: 0.20779895782470703  seconds.

Іноді потрібно отримати саме значення днів, годин і т. д. Із модуля time такі значення можна отримати як об’єкти struct_time:

import time
now = time.time()
print(time.localtime(now))
print(time.gmtime(now))

У даному прикладі, використані:

  • функція localtime() - представляє час у вашому поточному годинному поясі

  • функція gmtime() - представляє час у форматі UTC

Результатом використання останніх двох функцій будуть такими:

time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=13, tm_min=54, tm_sec=56, tm_wday=4, tm_yday=195, tm_isdst=1)
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=54, tm_sec=56, tm_wday=4, tm_yday=195, tm_isdst=0)

Дату й час можна перетворювати за допомогою функції strftime() з модуля time, яка у виведенні дати й часу використовує специфікатори виведення, показані у таблиці:

Таблиця "Специфікатори виведення для функціії strftime()"
Специфікатор Одиниця дати/часу Діапазон

%Y

Рік

1900-…​.

%m

Місяць

01-12

%B

Назва місяця

Січень, …​.

%d

День місяця

01-31

Назва дня

Неділя, …​.

Години (24 години)

00-23

%M

Хвилини

00-59

%S

Секунди

00-59

%f

Мікросекунди

000000, 000001, ..., 999999

Розглянемо приклад роботи функції strftime(), наданої модулем time. Вона перетворює об’єкт struct_time у рядок. Визначимо рядок формату під назвою row_format, використавши специфікатори формату з таблиці, і використаємо функцію localtime() з модуля time:

import time
row_format = "It's %A, %B %d, %Y, local time %H:%M:%S"
t = time.localtime()
print(t)
print(time.strftime(row_format, t))

Результат роботи функції strftime():

time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=13, tm_min=58, tm_sec=31, tm_wday=4, tm_yday=195, tm_isdst=1)
It's Friday, July 14, 2017, local time 13:58:31

Якщо ми спробуємо зробити це з об’єктом date,

from datetime import date
some_day = date(2017, 3, 4)
row_format = "It's %B %d, %Y, local time %I:%M:%S%p"
print(some_day.strftime(row_format))

функція відпрацює тільки для дати, час буде встановлено в опівніч:

It's March 04, 2017, local time 12:00:00AM

Для об’єкта time

from datetime import time
row_format = "It's %B %d, %Y, local time %I:%M:%S%p"
some_time = time(16, 40)
print(some_time.strftime(row_format))

будуть перетворені тільки частини, що стосуються часу:

It's January 01, 1900, local time 04:40:00PM

Є інший шлях для перетворення рядка на дату або час. Для цього використовують функцію strptime() з рядком формату row_format. Рядок з датою і часом повинен точно збігатися із частинами рядка формату. Зазначимо формат «рік-місяць-день», на зразок 2017-07-18:

import time
row_format = "%Y-%m-%d"
print(time.strptime("2017-07-18", row_format))
print(time.strptime("2017-07-18", row_format)[6]) # 0 = Monday (0 = Понеділок)
print(time.strptime("2017-07-18", row_format)[7]) # номер дня у році

Результати виведення будуть такими:

time.struct_time(tm_year=2017, tm_mon=7, tm_mday=18, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=1, tm_yday=199, tm_isdst=-1)
1
199

Складемо програму, яка повідомляє дату вашого народження для ваших міжнародних друзів з Facebook. На екран буде друкуватися дата дня народження (місяць, число і день тижня) різними мовами.

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

Дізнатися більш детально про механізм локалізації, ознайомитись з файлом модуля locale можна в офіційній документації Python.

Щоб визначити, яка локаль у вас за замовчуванням, виконайте такий код:

import locale
print(locale.getdefaultlocale())

Результатом роботи попереднього фрагменту коду буде кортеж, наприклад, з такими значеннями:

('uk_UA', 'cp1251')

Перший рядок кортежу - назва локалі ('uk_UA'), другий рядок - кодування (cp1251).

Назва локалі складається з двохсимвольного коду мови, знака підкреслення, двохсимвольного коду країни.

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

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

import locale, datetime
happy = datetime.date(2017, 3, 4)
print(happy.strftime('%A, %B %d'))
for lang_country in ['Belarusian_Belarus.1251', 'French_France.1252', 'Icelandic_Iceland.1252', 'Dutch_Netherlands.1252', 'Ukrainian_Ukraine.1251', 'Thai_Thailand.874', 'Polish_Poland.1250', 'pl.UTF-8', 'Maori.1252', 'Latvian_Latvia.1257']:
    try:
        locale.setlocale(locale.LC_ALL, lang_country)
        print(happy.strftime('%A, %B %d'))
    except locale.Error:
        print('unsupported locale setting')
    except UnicodeEncodeError:
        print('can\'t encode characters')

Результатом роботи програми буде:

Saturday, March 04
субота, сакавік 04
samedi, mars 04
laugardagur, mars 04
zaterdag, maart 04
субота, березень 04
เสาร์, มีนาคม 04
sobota, marzec 04
unsupported locale setting
unsupported locale setting
sestdiena, marts 04

Значення усіх lang_country можна також дізнатися використовуючи такий код:

names = locale.locale_alias.keys()
print(names)

А тепер отримаємо імена тільки тих локалей, які будуть працювати з функцією setlocale(), доповнивши попередній код:

good_names = [name for name in names if len(name) == 5 and name[2] == '_']
print(good_names)

Якщо необхідно отримати значення локалей для конкретної країни, наприклад Франції, доповніть попередній код рядками:

fr = [name for name in good_names if name.startswith('fr')]
print(fr)

Список локалей для Франції виявиться таким:

['fr_be', 'fr_ch', 'fr_fr', 'fr_ca', 'fr_lu']

9.3. Завдання

9.3.1. Контрольні запитання

  1. Відносно чого задається відносний шлях?

  2. З чого починається абсолютний шлях?

  3. Яке призначення функцій os.getcwd() і os.chdir()?

  4. Що позначають імена . і ..?

  5. Які частини шляху C:\Users\gt\logging\log12042017.txt представляють імена папок і назву файлу?

  6. Які функціії використовуються для копіювання, перейменування і видалення файлів і папок?

  7. Що таке «епоха Unix«?

  8. Яка функція повертає кількість секунд, які пройшли з моменту початку «епохи Unix»?

  9. У чому різниця між об’єктами datetime і timedelta?

  10. Які інструменти локалізації присутні у Python?

9.3.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

  1. Виведіть на екран повний шлях і список файлів поточного каталогу.

  2. Виведіть на екран розмір у байтах будь-якого файлу у каталозі.

  3. Здійсніть пошук файлів у поточному каталозі за певним шаблоном і виведіть результати на екран.

  4. У поточному каталозі вручну створіть два текстові файли, перший з яких містить довільний текст, другий - порожній. Використовуючи функції для роботи з файлами, виконайте такі дії: виконайте копіювання даних з першого файлу у другий, зчитайте з другого файлу скопійовані дані і виведіть їх на екран, а потім видаліть перший файл.

  5. Виведіть на екран поточну дату й час.

  6. Виведіть на екран кількість днів і років, які пройшли з початку «епохи Unix». Вважати 1 рік = 365 днів.

9.3.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

  1. Запишіть програмно поточні дату й час як рядок у текстовий файл today.txt. Зчитайте текстовий файл today.txt і розмістіть дані у рядку today_string. Розберіть дату з рядка today_string на складові, використовуючи функції для роботи з датом й часом, і виведіть їх на екран у вигляді повідомлень з пояснюючим текстом.

  2. Створіть об’єкт date, що містить дату вашого народження. Використайте функції для роботи з датою і часом, щоб дізнатися відповіді на такі питання: "Яка дата вашого народження?" "У який день тижня ви народилися?" "Коли вам буде (або вже було) 13 330 днів від народження?" Виведення організуйте в окремий файл у вигляді, на зразок «запитання: відповідь», у окремих рядках.

10. Класи

10.1. ООП і об’єкти

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

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

Усе в Python, від чисел до модулів, є об’єктами. Ви можете написати num = 7, щоб створити об’єкт типу int зі значенням 7, і присвоїти посилання на нього за іменем num.

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

Наприклад, цілочисельний об’єкт зі значенням 7 може використовувати методи на зразок додавання і множення. Число 8 - це вже інший цілочисельний об’єкт. Це означає що існує клас Integer, якому належать об’єкти 7 і 8.

Наприклад, String є вбудованим класом Python, який створює рядкові об’єкти на зразок 'Nairobi' і 'Singapore'. Рядки 'Nairobi' і 'Singapore' мають методи, наприклад, capitalize() і replace().

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

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

10.2. Створення і використання класу

Почнемо з написання простого класу Dog, що представляє собаку - не якусь конкретну, а собаку взагалі. Що ми знаємо про собак? У них є кличка і вік. Також відомо, що більшість собак вміють сідати і перекочуватися по команді. Ці два види інформації (кличка і вік) і два види поведінки (сидіти і перекочуватися) будуть включені в клас Dog, тому що вони є загальними для більшості собак.

Клас повідомляє Python, як створити об’єкт, який представляє собаку. Після того як клас буде написаний, ми використовуємо його для створення екземплярів, кожен з яких представлятиме одну конкретну собаку.

10.2.1. Створення класу

Функція, що є частиною класу, називається методом. Практична відмінність між функціями і методами - спосіб виклику методів.

У кожному екземплярі, створеному на основі класу Dog, буде зберігатися кличка і вік; крім того, у ньому будуть присутні методи sit() і roll_over():

class Dog(): (1)
    """Проста модель собаки.""" (2)
    def __init__(self, name, age): (3)
        """Ініціалізація атрибутів name і age."""
        self.name = name (4)
        self.age = age

	def sit(self): (5)
        """Собака сідає по команді."""
        print(self.name.title() + " is now sitting.")

	def roll_over(self):
        """Собака перекочується по команді."""
        print(self.name.title() + " rolled over!")
  1. Визначається клас з ім’ям Dog. Імена для класів у Python починаються з великої літери.

  2. Записано коментар з коротким описом класу.

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

    1. Ім’я методу починається і закінчується двома символами підкреслення. Така схема запису унеможливлює конфлікти імен стандартних методів Python і методів власних класів.

    2. Метод __init__ визначається з трьома параметрами у дужках: self, name та age.

    3. Параметр self є обов’язковим у визначенні методу, він повинен бути першим у списку усіх параметрів.

    4. self є включеним у визначення методу для того, щоб у майбутньому виклику методу __init__ (для створення екземпляра класу Dog) автоматично передавався аргумент self.

    5. При кожному виклику методу, пов’язаного з класом, автоматично передається self - посилання на екземпляр. Посилання надає конкретному екземпляру доступ до атрибутів і методів класу.

    6. Коли ви створюєте екземпляр класу Dog, Python викликає метод __init__і з класу Dog.

    7. Ми передаємо класу Dog() кличку та вік в аргументах, значення self передається автоматично (його передавати не потрібно).

    8. Кожного разу, коли ви захочете створити екземпляр на основі класу Dog, необхідно надавати значення лише двох останніх аргументів name і age.

  4. Кожна із двох змінних має префікс self.

    1. Будь-яка змінна з префіксом self доступна для кожного методу у класі, і можна звернутися до цих змінних у кожному екземплярі, створеному на основі класу.

    2. Конструкція self.name = name отримує значення, яке зберігається в параметрі name, і зберігає його в змінну name, яка потім зв’язується зі створюваним екземпляром.

    3. Процесс також повторюється з self.age = age.

    4. Змінні, до яких звертаються через экземпляри, теж називаються атрибутами.

  5. В класі Dog також визначаються два метода: sit() і roll_over().

    1. Так як цим методам не потрібна додаткова інформація (кличка або вік), вони визначаються з єдиним параметром self.

    2. Екземпляри, які будуть створені пізніше, зможуть викликати ці методи.

    3. Поки методи sit() і roll_over() обмежуються простим виведенням повідомлення про те, що собака сідає або перекочується.

10.2.2. Створення екземпляру класу

Можна вважати, що клас - це, свого роду, інструкція по створенню екземплярів. Відповідно, клас Dog - інструкція по створенню екземплярів, які представляють конкретних собак.

Створимо екземпляр, який представляє конкретну собаку, використовуючи клас Dog:

my_dog = Dog('jessie', 5) (1)
print("My dog's name is " + my_dog.name.title() + ".") (2)
print("My dog is " + str(my_dog.age) + " years old.") (3)
  1. Cтворюється екземпляр собаки з кличкою Jessie і віком 5 років.

    1. У процесі обробки цього рядка Python викликає метод init класу Dog з аргументами 'Jessie' і 5.

    2. Метод __init__ створює екземпляр, який представляє конкретну собаку, і присвоєю атрибутам name і age цього екземпляру передані значення.

    3. Метод __init__ не містить явної команди return, але Python автоматично повертає екземпляр, який представляє собаку. Цей екземпляр зберігається у змінну my_dog.

Зазвичай, вважається, що ім’я, яке починається з символу верхнього регістра (наприклад, Dog), позначає клас, а ім’я, записане в нижньому регістрі (наприклад, my_dog), позначає окремий екземпляр, створений на основі класу.

10.2.3. Доступ до атрибутів

Для звернення до атрибутів екземпляра використовується точковий запис.
  1. Звертаються до значення атрибута name екземпляра my_dog:

my_dog.name
  1. Точковий запис часто використовується у Python. Цей синтаксис показує, як Python шукає значення атрибутів.

  2. В даному випадку Python звертається до екземпляру my_dog і шукає атрибут name, пов’язаний з екземпляром my_dog.

  3. Це той самий атрибут, який позначався self.name в класі Dog.

  1. Використовується такий самий прийом для роботи з атрибутом age. У першій команді print виклик:

my_dog.name.title()

записує ім’я собаки 'jessie' (значення атрибута name екземпляра my_dog) з великої літери. У другій команді print виклик:

str(my_dog.age)

перетворює 5, значення атрибута age екземпляра my_dog, у рядок. Даний приклад виводить зведення відомих фактів про my_dog:

My dog's name is Jessie.
My dog is 5 years old.

10.2.4. Виклик методів

Після створення екземпляра на основі класу Dog можна застосовувати точковий запис для виклику будь-яких методів, визначених у Dog.

Щоб викликати метод, вкажіть екземпляр (у даному випадку my_dog) і метод, який викликається, розділивши їх крапкою.

my_dog.sit()
my_dog.roll_over()

В ході обробки my_dog.sit() Python шукає метод sit() в класі Dog і виконує його код. Рядок my_dog.roll_over() інтерпретується аналогічним чином. Тепер екземпляр слухняно виконує отримані команди:

Jessie is now sitting.
Jessie rolled over!

10.2.5. Створення декількох екземплярів

На основі класу можна створити стільки екземплярів, скільки вам буде потрібно. Створимо другий екземпляр класу Dog з ім’ям your_dog:

my_dog = Dog('jessie', 5)
your_dog = Dog('jack', 2)

print("My dog's name is " + my_dog.name.title() + ".")
print("My dog is " + str(my_dog.age) + " years old.")
my_dog.sit()

print("\nYour dog's name is " + your_dog.name.title() + ".")
print("Your dog is " + str(your_dog.age) + " years old.")
your_dog.sit()

У цьому прикладі створюються два екземпляри з іменами Jessie і Jack. Кожен з них має свій набір атрибутів і здатний виконувати дії із загального набору:

My dog's name is Jessie.
My dog is 5 years old.
Jessie is now sitting.

Your dog's name is Jack.
Your dog is 2 years old.
Jack is now sitting.

10.3. Робота з класами та екземплярами

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

Атрибути екземпляра можна змінювати безпосередньо або ж написати методи, які змінюють атрибути за особливими правилами.

Напишемо клас, що представляє автомобіль. Цей клас буде містити інформацію про тип машини, а також метод для виведення короткого опису:

class Car():
    """Проста модель автомобіля."""
    def __init__(self, make, model, year): (1)
        """Ініціалізація атрибутів опису автомобіля."""
        self.make = make
        self.model = model
        self.year = year

    def get_descriptive_name(self): (2)
        """Повертає опис у відфорамтованому вигляді."""
        long_name = str(self.year) + ' ' + self.make + ' ' + self.model
        return long_name.title()

my_new_car = Car('citroen', 'c3', 2017) (3)
print(my_new_car.get_descriptive_name())
  1. В класі Car визначається метод __init__.

    1. Його список параметрів починається з self, як і у класі Dog.

    2. За ним слідують ще три параметра: make, model і year.

    3. Метод __init__ отримує ці параметри і зберігає їх в атрибутах, які будуть пов’язані з екземплярами, створеними на основі класу.

    4. При створенні нового екземпляра класу Car необхідно вказати фірму-виробника, модель і рік випуску для даного екземпляра.

  2. Визначається метод get_descriptive_name(), який об’єднує рік випуску, фірму-виробника і модель в один рядок з описом.

    1. Це дозволяє виводити значення кожного атрибута не окремо, а цілим рядком.

    2. Для роботи зі значеннями атрибутів в цьому методі використовується синтаксис self.make, self.model і self.year.

  3. Cтворюється екземпляр класу Car, який зберігається у змінну my_new_car.

    1. Виклик методу get_descriptive_name() показує, з яким автомобілем працює програма:

2017 Citroen C3

10.3.1. Присвоювання атрибуту значення за замовчуванням

Кожен атрибут класу повинен мати початкове значення, навіть якщо воно дорівнює 0 або є порожнім рядком. У деяких випадках (наприклад, при встановленні значень за замовчуванням) це початкове значення є сенс ставити в тілі методу __init__. У такому випадку передавати параметр для цього атрибута при створенні об’єкта не обов’язково.

Додамо у клас Car атрибут, що змінюється з часом - пробіг автомобіля в кілометрах.

Додамо у клас атрибут з ім’ям odometer_reading, початкове значення якого завжди дорівнює 0. Також у клас буде включений метод read_odometer() для читання поточних показань одометра:

class Car():
    """Проста модель автомобіля."""
    def __init__(self, make, model, year):
        """Ініціалізація атрибутів опису автомобіля."""
        self.make = make
        self.model = model
        self.year = year
		self.odometer_reading = 0 (1)

    def get_descriptive_name(self):
        """Повертає опис у відформатованому вигляді."""
        long_name = str(self.year) + ' ' + self.make + ' ' + self.model
        return long_name.title()

	def read_odometer(self): (2)
		"""Виводить пробіг автомобіля у км."""
		print("This car has " + str(self.odometer_reading) + " kilometers on it.")

my_new_car = Car('citroen', 'c3', 2017)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer() (3)
  1. Коли Python викликає метод __init__ для створення нового екземпляра my_new_car, цей метод зберігає фірму-виробника, модель і рік випуску в атрибутах, як і у попередньому випадку. Потім Python створює новий атрибут з ім’ям odometer_reading і присвоює йому початкове значення 0.

  2. Також у клас додається новий метод read_odometer(), який спрощує читання пробігу автомобіля в кілометрах.

  3. Виклик методів get_descriptive_name() і read_odometer() для екземпляра my_new_car дає такі початкові дані для нового автомобіля:

2017 Citroen C3
This car has 0 kilometers on it.

10.3.2. Зміна значень атрибутів

Значення атрибута у нашій задачі можна змінити одним з трьох способів:

  • змінити його прямо в екземплярі

  • задати значення або змінити його за допомогою методу

Розглянемо ці способи.

Щоб змінити значення атрибута, найпростіше звернутися до нього прямо через екземпляр. У наступному прикладі на одометрі безпосередньо виставляється значення 120:

my_new_car.odometer_reading = 120
my_new_car.read_odometer()

Точковий запис використовується для звернення до атрибуту odometer_reading екземпляра і прямого присвоєння йому значення. Рядок my_new_car.odometer_reading = 120 вказує Python взяти екземпляр my_new_car, знайти пов’язаний з ним атрибут odometer_reading і задати значення атрибута рівним 120:

2017 Citroen C3
This car has 120 kilometers on it.

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

У наступному прикладі в кінець класу включається метод update_odometer() для зміни показів одометра:

class Car():
	...

    def update_odometer(self, km): (1)
        """Встановлення заданого значення на одометрі."""
        self.odometer_reading = km

my_new_car = Car('citroen', 'c3', 2017)
print(my_new_car.get_descriptive_name())

my_new_car.update_odometer(120) (2)
my_new_car.read_odometer() (3)
  1. Клас Car майже не змінився, в ньому тільки додався метод update_odometer(). Цей метод отримує пробіг в кілометрах і зберігає його в self.odometer_reading.

  2. Викликається метод update_odometer() і йому передається значення 120 в аргументі (відповідному параметру km у визначенні методу). Метод встановлює на одометрі значення 120.

  3. Метод read_odometer() доповнює інформацію про автомобіль показниками пробігу:

2017 Citroen C3
This car has 120 kilometers on it.

Метод update_odometer() можна розширити так, щоб при кожній зміні показників одометра виконувалася деяка додаткова робота. Додамо перевірку, яка гарантує, що ніхто не буде намагатися скидати показники одометра:

class Car():
	...

    def update_odometer(self, km):
        """Встановлення заданого значення на одометрі.
        При спробі зменшення пробігу зміна відхиляється."""
        if km >= self.odometer_reading: (1)
            self.odometer_reading = km
        else:
            print("You can't roll back an odometer!") (2)

my_new_car.update_odometer(120)
my_new_car.read_odometer()
my_new_car.update_odometer(110)
  1. Тепер update_odometer() перевіряє нове значення перед зміною атрибута. Якщо нове значення km більше або дорівнює поточному self.odometer_reading, показники одометра можна оновити новим значенням.

  2. Якщо ж нове значення менше поточного, ви отримаєте попередження про неприпустимість скидання показників.

2017 Citroen C3
This car has 120 kilometers on it.
You can't roll back an odometer!

Іноді значення атрибута потрібно змінити з заданим збільшенням (замість того, щоб присвоювати атрибуту довільне нове значення).

Припустимо, ви придбали машину, яка була у вжитку, і проїхали на ній 100 км. Наступний метод отримує величину приросту і додає її до поточних показників одометра:

class Car():
	...

    def increment_odometer(self, kkm): (1)
        """Збільшує показання одометра із заданим збільшенням."""
        self.odometer_reading += kkm

my_used_car = Car('toyota', 'corolla', 2015) (2)
print(my_used_car.get_descriptive_name())

my_used_car.update_odometer(25700) (3)
my_used_car.read_odometer()

my_used_car.increment_odometer(100) (4)
my_used_car.read_odometer()
  1. Новий метод increment_odometer() отримує відстань в кілометрах kkm і додає її до self.odometer_reading.

  2. Cтворюється екземпляр my_used_car.

  3. Показники його одометра встановлюється у значення 25700, для цього викликається метод update_odometer(), якому передається значення 25700.

  4. Викликається метод increment_odometer(), якому передається значення 100, щоб збільшити показники лічильника шляху на 100 км, пройдені з моменту покупки:

2015 Toyota Corolla
This car has 25700 kilometers on it.
This car has 25800 kilometers on it.

10.4. Наслідування

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

Один клас, що успадковує від іншого, автоматично отримує всі атрибути і методи першого класу.

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

Перше, що робить Python при створенні екземпляра класу-нащадка, - додає значення всіх атрибутів класу-батька. Для цього методу __init__ класу-нащадка необхідна допомога з боку класу-батька.

Наприклад, спробуємо побудувати модель електромобіля. Електромобіль передставляє собою спеціалізований різновид автомобіля, тому новий клас ElectricCar (нащадок) можна створити на базі класу Car (батько), написаного раніше. Тоді нам залишиться додати в нього код атрибутів і поведінки, що відноситься тільки до електромобілів.

Почнемо із створення простої версії класу ElectricCar, який робить все, що робить клас Car:

class Car(): (1)
    """Проста модель автомобіля."""
    def __init__(self, make, model, year):
        """Ініціалізація атрибутів опису автомобіля."""
        self.make = make
        self.model = model
        self.year = year
        self.odometer_reading = 0

    def get_descriptive_name(self):
        """Повертає опис у відформатованому вигляді."""
        long_name = str(self.year) + ' ' + self.make + ' ' + self.model
        return long_name.title()

    def read_odometer(self):
        """Виводить пробіг автомобіля у км."""
        print("This car has " + str(self.odometer_reading) + " kilometers on it.")

    def update_odometer(self, km):
        """Встановлення заданого значення на одометрі.
        При спробі зворотного прокручення зміна відхиляється."""
        if km >= self.odometer_reading:
            self.odometer_reading = km
        else:
            print("You can't roll back an odometer!")

    def increment_odometer(self, kkm):
        """Збільшує показання одометра із заданим збільшенням."""
        self.odometer_reading += kkm

class ElectricCar(Car): (2)
    """Представляє аспекти автомобіля, специфічні для електромобілів."""
    def __init__(self, make, model, year): (3)
        """Ініціалізує атрибути класу-батька."""
        super().__init__(make, model, year) (4)

my_tesla = ElectricCar('tesla', 'model s', 2018) (5)
print(my_tesla.get_descriptive_name())
  1. Створюється екземпляр Car. При створенні класу-нащадка клас-батько повинен бути частиною поточного файлу, а його визначення повинно передувати визначення класу-нащадка у файлі.

  2. Визначається клас-нащадок ElectricCar. У визначенні нащадка ім’я класу-батька записується у круглих дужках.

  3. Метод __init__ отримує інформацію, необхідну для створення екземпляра Car.

  4. Функція super() - спеціальна функція, яка допомагає Python зв’язати нащадка з батьком.

    1. Цей рядок вказує Python викликати метод __init__ класу, який є батьком ElectricCar, в результаті чого екземпляр класу ElectricCar отримує всі атрибути класу-батька.

    2. Ім’я super виходить з такої термінології: клас-батько називається суперкласом, а клас-нащадок - субкласом.

  5. Щоб перевірити, чи правильно спрацювало наслідування, спробуємо створити електромобіль з такою ж інформацією, яка передається при створенні звичайного екземпляра Car.

    1. Cтворюємо екземпляр класу ElectricCar і зберігаємо його у змінну my_tesla.

    2. Цей рядок викликає метод __init__, який визначений у класі ElectricCar, який в свою чергу вказує Python викликати метод init, визначений в класі-батька Car.

    3. При виклику передаються аргументи 'tesla', 'model s' і 2018.

    4. Крім __init__ клас ще не містить ніяких атрибутів або методів, специфічних для електромобілів.

    5. Поки ми просто переконуємося в тому, що клас електромобіля містить всю поведінку, властиву класу звичайного автомобіля:

2018 Tesla Model S

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

Додамо атрибут, специфічний для електромобілів (наприклад, потужність акумулятора), і метод для виведення інформації про цей атрибут:

class Car():
    ...

class ElectricCar(Car):
    """Представляє аспекти автомобіля, специфічні для електромобілів."""
    def __init__(self, make, model, year):
        """Ініціалізує атрибути класу-батька.
        Потім ініціалізує атрибути, специфічні для електромобіля."""
        super().__init__(make, model, year)
        self.battery_size = 70 (1)

    def describe_battery(self): (2)
        """Виводить інформацію про потужність акумулятора."""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")

my_tesla = ElectricCar('tesla', 'model s', 2018)
print(my_tesla.get_descriptive_name())
my_tesla.describe_battery() (3)
  1. Додається новий атрибут self.battery_size, якому присвоюється початкове значення - скажімо, 70. Цей атрибут буде присутній у всіх екземплярах, створених на основі класу ElectricCar (але не у всякому екземплярі Car).

  2. Також додається метод з ім’ям describe_battery(), який виводить інформацію про акумулятор.

  3. При виклику методу describe_battery() виводиться опис, який явно відноситься тільки до електромобілів:

2018 Tesla Model S
This car has a 70-kWh battery.

Можливості спеціалізації класу ElectricCar безмежні. Ви можете додати скільки завгодно атрибутів і методів, щоб моделювати електромобіль з будь-якими властивостями. Атрибути або методи, які можуть належати будь-якій машині (а не тільки електромобілю), повинні додаватися до класу Car. Тоді ця інформація буде доступна всім користувачам класу Сar, а клас ElectricCar буде містити тільки код інформації і поведінку, специфічні для електромобілів.

10.5. Перевизначення методу

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

Для цього в класі-нащадку визначається метод з тим же ім’ям, що і у методі класу-батька. Python ігнорує метод у класі батька і звертає увагу тільки на метод, визначений в нащадку.

Припустимо, в класі Car є метод fill_petrol_tank(). Для електромобілів заправка бензином безглузда, тому цей метод логічно перевизначити. Наприклад, це можна зробити так:

class Car():
    ...

    def fill_petrol_tank(self):
        """Виводить інформацію про наяність бака для бензину."""
        print("This car is equipped with a petrol tank.")

class ElectricCar(Car):
    ...

    def fill_petrol_tank(self):
        """У електромобілів немає бензобаку."""
        print("This car doesn't need a petrol tank!")

my_new_car = Car('citroen', 'c3', 2017)
print(my_new_car.get_descriptive_name())
my_new_car.fill_petrol_tank() (1)

my_tesla = ElectricCar('tesla', 'model s', 2018)
print(my_tesla.get_descriptive_name())
my_tesla.fill_petrol_tank() (2)
  1. Якщо викликати метод fill_petrol_tank() для звичайного автомобіля з бензобаком, Python викличе метод fill_gas_tank() класу Car.

  2. Якщо хтось спробує викликати метод fill_petrol_tank() для електромобіля, Python проігнорує метод fill_gas_tank() класу Car та виконає замість нього код із метода fill_gas_tank() класу ElectricCar:

2017 Citroen C3
This car is equipped with a petrol tank.
2018 Tesla Model S
This car doesn't need a petrol tank!
Із застосуванням наслідування нащадок зберігає ті аспекти батька, які вам потрібні, і перекриває все непотрібне.

10.6. Екземпляри як атрибути

При моделюванні явищ реального світу у програмах класи нерідко доповнюють все більшою кількістю подробиць. Списки атрибутів і методів ростуть, і через якийсь час файли стають громіздкими.

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

Наприклад, при подальшій доробці класу ElectricCar може виявитися, що в ньому з’явилося занадто багато атрибутів і методів, що відносяться до акумулятора. В такому випадку можна зупинитися і перемістити всі ці атрибути і методи в окремий клас з ім’ям Battery. Таким чином, екземпляр Battery стає атрибутом класу ElectricCar.

class Car():
    ...

class Battery(): (1)
    """Проста модель акумулятора електромобіля."""
    def __init__(self, battery_size=70): (2)
        """Ініціалізує атрибути акумулятора."""
        self.battery_size = battery_size

    def describe_battery(self): (3)
        """Виводить інформацію про потужність акумулятора."""
        print("This car has a " + str(self.battery_size) + "-kWh battery.")

class ElectricCar(Car):
    """Представляє аспекти автомобіля, специфічні для електромобілів."""
    def __init__(self, make, model, year):
        """Ініціалізує атрибути класу-батька.
        Потім ініціалізує атрибути, специфічні для електромобіля."""
        super().__init__(make, model, year)
        self.battery = Battery() (4)

    def fill_petrol_tank(self):
        """У електромобілів немає бензобаку."""
        print("This car doesn't need a petrol tank!")

my_tesla = ElectricCar('tesla', 'model s', 2018) (5)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery() (6)
  1. Визначається новий клас з ім’ям Battery, який не буде наслідувати ні від одного з інших класів.

  2. Метод __init__ отримує * self* і один параметр за замовчуванням battery_size. Якщо значення не надано, цей параметр за замовчуванням задає battery_size значення 70.

  3. Метод describe_battery() також переміщений у клас Battery.

  4. Потім в клас ElectricCar додається атрибут з ім’ям self.battery.

    1. Цей рядок вказує Python створити новий екземпляр Battery (зі значенням battery_size за замовчуванням, рівним 70, тому що значення не задано) і зберегти його в атрибуті self.battery.

    2. Це буде відбуватися при кожному виклику __init__ і будь-який екземпляр ElectricCar матиме автоматично створюваний екземпляр Battery.

  5. Програма створює екземпляр електромобіля і зберігає його у змінній my_tesla.

  6. Коли буде потрібно вивести опис акумулятора, необхідно звернутися до екземпляра my_tesla, знайти атрибут battery і викликати метод describe_battery(), який зв’язаний з екземпляром Battery із атрибуту:

2018 Tesla Model S
This car has a 70-kWh battery.

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

class Car():
    ...

class Battery():
    ...

    def get_range(self): (1)
        """Виводить приблизний запас ходу для акумулятора."""
        if self.battery_size == 70:
            range = 240
        elif self.battery_size == 85:
            range = 270
        message = "This car can go approximately " + str(range)
        message += " kilometers on a full charge."
        print(message)

class ElectricCar(Car):
	...

my_tesla = ElectricCar('tesla', 'model s', 2018)
print(my_tesla.get_descriptive_name())
my_tesla.battery.describe_battery()
my_tesla.battery.get_range() (2)
  1. Новий метод get_range() проводить простий аналіз.

    1. Якщо потужність дорівнює 70, то get_range() встановлює запас ходу 240` км, а при потужності 85 kWh запас ходу дорівнює 270 км.

    2. Потім програма виводить це значення.

  2. Коли ви захочете використовувати метод get_range(), його доведеться викликати через атрибут battery.

Результат повідомляє запас ходу автомобіля в залежності від потужності акумулятора:

2018 Tesla Model S
This car has a 70-kWh battery.
This car can go approximately 240 kilometers on a full charge.

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

10.7. Завдання

10.7.1. Контрольні запитання

Дайте пояснення таких понять у Python:

  • об’єкт

  • клас

  • екземпляр класу

  • метод класу

  • атрибут класу

  • наслідування

  • перевизначення методу

  • екземпляри як атрибути

10.7.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

  1. Створіть клас, який називається SportResults, який не має вмісту, і виведіть його на екран. Потім створіть екземпляр diving цього класу і також виведіть його. Чи збігаються виведені значення?

  2. У клас SportResults додайте змінну points зі значенням 25 і виведіть на екран значення атрибуту points для екземпляру diving. А потім надайте значення 150 для points. Знову виведіть на екран значення атрибуту points для екземпляру diving. Чи потрібно було створювати новий екземпляр класу, щоб зробити повторне виведення?

  3. Створіть клас, який називається Element, з методом __init__, що має атрибути екземпляру name, symbol і number. Створіть екземпляр цього класу el зі значеннями 'Silicium', 'Si' і 14 і виведіть на екран значення його атрибутів.

  4. Створіть словник з наступними ключами і значеннями: 'name': 'Argentum', 'symbol': 'Ag', 'number': 47. Далі створіть екземпляр з ім’ям argentum класу Element за допомогою цього словника і виведіть значення усіх атрибутів.

  5. Для класу Element визначте метод з ім’ям dump(), який виводить на екран значення атрибутів екземпляру (name, symbol і number). Створіть екземпляр argentum з цього нового визначення і використайте метод dump(), щоб вивести на екран його атрибути.

  6. Визначте три класи: Switzerland, England і Japan. Для кожного з них визначте всього один метод - currency(). Метод повинен повертати значення 'franc' (для Switzerland), 'pound' (для England) або 'yen' (для Japan). Створіть по одному екземпляру кожного класу і виведіть на екран грошову одиницю кожної країни.

  7. Визначте три класи: Printer, Lamp і Car. Кожен з них має тільки один метод - does(). Він повертає значення 'print' (для Printer), 'glow' (для Lamp) або 'ride' (для Car). Далі визначте клас Robot з методом __init__, який містить по одному екземпляру кожного з цих класів як власні атрибути. Визначте метод do_it() для класу Robot, який виводить на екран усі дії, що роблять його компоненти.

10.7.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

  1. Онлайн-магазин.

    1. Створіть клас з ім’ям Shop(). Метод __init__() класу Shop() повинен містити два атрибути: shop_name і store_type. Створіть метод describe_shop(), який виводить два атрибути, і метод open_shop(), який виводить повідомлення про те, що онлайн-магазин відкритий. Створіть на основі класу екземпляр з ім’ям store. Виведіть два атрибути окремо, потім викличте обидва методи.

    2. Створіть три різних екземпляри класу, викличте для кожного екземпляру метод describe_shop().

    3. Додайте атрибут number_of_units зі значенням за замовчуванням 0; він представляє кількість видів товару у магазині. Створіть екземпляр з ім’ям store. Виведіть значення number_of_units, а потім змініть number_of_units і виведіть знову.

    4. Додайте метод з ім’ям set_number_of_units(), що дозволяє задати кількість видів товару. Викличте метод з новим числом, знову виведіть значення. Додайте метод з ім’ям increment_number_of_units(), який збільшує кількість видів товару на задану величину. Викличте цей метод.

    5. Напишіть клас Discount(), що успадковує від класу Shop(). Додайте атрибут з ім’ям discount_products для зберігання списку товарів, на які встановлена знижка. Напишіть метод get_discounts_ptoducts, який виводить цей список. Створіть екземпляр store_discount і викличте цей метод.

    6. Збережіть код класу Shop() у модулі. Створіть окремий файл, що імпортує клас Shop(). Створіть екземпляр all_store і викличте один з методів Shop(), щоб перевірити, що команда import працює вірно.

  2. Облік користувачів на сайті.

    1. Створіть клас з ім’ям User. Створіть два атрибути first_name і last_name*, а потім ще кілька атрибутів, які зазвичай зберігаються у профілі користувача. Напишіть метод describe_user який виводить повне ім’я користувача. Створіть ще один метод greeting_user() для виведення персонального вітання для користувача. Створіть кілька примірників, які представляють різних користувачів. Викличте обидва методи для кожного користувача.

    2. Додайте атрибут login_attempts у клас User. Напишіть метод increment_login_attempts(), що збільшує значення login_attempts на 1. Напишіть інший метод з ім’ям reset_login_attempts(), обнуляє значення login_attempts. Створіть екземпляр класу User і викличте increment_login_attempts() кілька разів. Виведіть значення login_attempts, щоб переконатися у тому, що значення було змінено правильно, а потім викличте reset_login_attempts(). Знову виведіть login_attempts і переконайтеся у тому, що значення обнулилося.

    3. Адміністратор - особливий різновид користувача. Напишіть клас з ім’ям Admin, що успадковує від класу User. Додайте атрибут privileges для зберігання списку рядків виду «Allowed to add message», «Allowed to delete users», «Allowed to ban users» і т. д. Напишіть метод show_privileges() для виведення набору привілеїв адміністратора. Створіть екземпляр Admin і викличте метод.

    4. Напишіть клас Privileges. Клас повинен містити всього один атрибут privileges зі списком, який треба забрати із класу Admin. Водночас, необхідно перемістити метод show_privileges() у клас Privileges із класу Admin. Створіть екземпляр priv як атрибут класу Admin. Створіть новий екземпляр admin і використайте метод для виведення списку привілеїв.

    5. Збережіть клас User в одному модулі, а класи Privileges і Admin у іншому модулі. В окремому файлі створіть екземпляр admin і викличте метод show_privileges(), щоб перевірити, що все працює вірно.

11. Робота з даними

11.1. Текстові дані

11.1.1. ASCII

В усіх прикладах коду даного підручника текстові рядки мали формат ASCII, який був визначений в 1960-х роках. Основною одиницею зберігання інформації був байт, який міг зберігати 256 унікальних значень у своїх 8 бітах.

З різних причин формат ASCII використовував тільки 7 біт (128 унікальних значень): 26 символів верхнього регістру і 26 символів нижнього регістра англійського алфавіту, 10 цифр, деякі знаки пунктуації, символи пробілу і недруковані символи. На жаль, у світі існує більше букв, ніж надає формат ASCII.

Було зроблено багато спроб додати більше букв і символів, і час від часу з’являлись різні системи, на зразок таких:

  • Latin-1 або ISO 8859-1

  • Windows-1252

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

11.1.2. Unicode

Рядки у Python 3 є рядками формату Unicode, а не масивом байтів.

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

У Python вбудовані функції для роботи з символами і їхніми номерами в Unicode:

  • ord() - якщо рядок представляє один символ Unicode, функція повертає ціле число - код Unicode цього символу. Наприклад, ord('a') повертає ціле число 97 та ord('€') (символ Євро) повертає 8364.

  • chr() - повертає рядок у вигляді символу, за його кодом в Unicode. Наприклад, chr(97) повертає рядок 'a', а chr(8364) повертає рядок '€'.

Сторінка Unicode Code Charts містить посилання на всі визначені на даний момент набори символів з їхніми зображеннями.

Кожен із символів у форматі Unicode має унікальне ім’я та ідентифікаційний номер. Якщо ви знаєте Unicode ID або назву символу, то можете використовувати його у рядку Python згідно таких правил:

  1. Символ \u, за яким розташовуються чотири шістнадцяткові числа (числа шістнадцяткової системи числення, що містять символи від 0 до 9 і від A до F), визначають символ, що знаходиться в одній з 256 багатомовних площин Unicode.

    1. Перші два числа є номером площини (від 00 до FF), а наступні два - індексом символу всередині площини.

    2. Площина з номером 00 - це старий добрий формат ASCII, і позиції символів у ньому такі ж, як і у ASCII.

  2. Для символів вищих площин потрібно більше бітів.

    1. Керуюча послідовність для них виглядає як \U, за яким слідують вісім шістнадцяткових символів.

    2. Крайній зліва з них має дорівнювати 0.

  3. Для усіх символів конструкція \N{ім’я} дозволяє вказати символ за допомогою його стандартного імені.

Модуль unicodedata містить функції, які перетворюють символи в обох напрямках:

  • lookup() приймає незалежне від регістру ім’я і повертає символ Unicode

  • name() приймає символ Unicode і повертає його ім’я у верхньому регістрі

У наступному прикладі ми напишемо перевірочну функцію, яка приймає рядок із символом Unicode, шукає його ім’я, а потім шукає символ, який відповідає отриманому імені (результат повинен співпасти з оригінальним). Спробуємо перевірити кілька символів, починаючи з простої букви і розділового знаку у форматі ASCII:

Наступні приклади наведені з використанням інтерактивного інтерпретатора Python.
>>> def unicode_test(value):
...     import unicodedata
...     name_unicode = unicodedata.name(value)
...     value2 = unicodedata.lookup(name_unicode)
...     print('value="{0:s}", name_unicode="{1:s}", value2="{2:s}"'.format(value, name_unicode, value2))
...
>>> unicode_test('P')
value="P", name_unicode="LATIN CAPITAL LETTER P", value2="P"
>>> unicode_test('!')
value="!", name_unicode="EXCLAMATION MARK", value2="!"

А тепер використаємо функцію для кількох рядків, що містять символи у форматі Unicode (символи валютних знаків \u00a2, \u20ac і '\u20B4') s:

>>> unicode_test('\u00a2')
value="¢", name="CENT SIGN", value2="¢"
>>> unicode_test('\u20ac')
value="€", name="EURO SIGN", value2="€"
>>> unicode_test('\u20B4')
value="₴", name="HRYVNIA SIGN", value2="₴"

Проблемою, з якою ви можете зіткнутися, працюючи із символами Unicode, є обмеження, які накладаються шрифтом. У жодному шрифті немає символів для усіх символів Unicode.

Припустимо, ми хочемо зберегти у рядку французьку назву місяця серпень - слово août . Одне з рішень: скопіювати назву з файлу або з сайту, де це слово записано вже у такому вигляді, і сподіватися, що це спрацює:

>>> month = 'août'
>>> month
'août'

Але як вказати, що останній символ - це «û»?

Символ над літерою u називається циркумфлексом.

Якщо подивитися на індекс символу «U», можна побачити, що повне ім’я символа U WITH CIRCUMFLEX, LATIN SMALL LETTER має індекс 00FB.

Розглянемо знову функції name() і lookup() із модуля unicodedata. Спочатку передамо код символу, щоб отримати його повне ім’я, але у зміненому вигляді:

>>> import unicodedata
>>> unicodedata.name('\u00FB')
'LATIN SMALL LETTER U WITH CIRCUMFLEX'

Тепер знайдемо справжнє ім’я в Unicode для отриманого повного імені:

>>> unicodedata.lookup('LATIN SMALL LETTER U WITH CIRCUMFLEX')
'û'

Тепер символ «û» можна використовувати як із допомогою коду символу, так і завдяки повному імені:

>>> month = 'ao\u00FBt'
>>> month
'août'
>>> month = 'ao\N{LATIN SMALL LETTER U WITH CIRCUMFLEX}t'
>>> month
'août'

11.1.3. UTF-8

Якби в Unicode було б менше 64 000 символів, тоді можна було б зберігати ID кожного з них у двох байтах. На жаль, символів більше. Можна кодувати кожен ID за допомогою трьох або чотирьох байтів, але це збільшує обсяг пам’яті і дискового простору, необхідний для звичайних текстових рядків, у три або чотири рази.

У таких випадках може знадобиться:

  • спосіб закодувати рядок за допомогою байтів

  • спосіб декодувати байти назад в рядок

Кен Томпсон (Ken Thompson) і Роб Пайк (Rob Pike) розробили UTF-8 - динамічну схему кодування. Вона використовує для символу Unicode від одного до чотирьох байтів:

  • один байт для ASCII-символів

  • два байта для більшості мов, заснованих на латиниці (але не кирилиці)

  • три байта для інших простих мов

  • чотири байти для решти мов, включаючи деякі азіатські мови і символи

UTF-8 - це стандартне текстове кодування для Python, Linux і HTML.
Якщо ви створюєте рядок Python шляхом копіювання символів з іншого джерела на зразок веб-сторінки, і їх вставки, переконайтеся, що джерело було закодоване за допомогою UTF-8. Дуже часто може виявитися, що текст був зашифрований за допомогою кодувань Latin-1 або Windows-1252 тощо, то при копіюванні у рядок Python викличе генерацію винятків через некоректні послідовності байтів.
Кодування

Рядок у форматі Unicode кодується байтами за допомогою функції encode(). Перший аргумент цієї функції - назва кодування. Види кодувань представлені у таблиці:

Таблиця "Види кодувань"

ascii

Cемибітне кодування ASCII

utf-8

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

latin-1

Також відома як ISO 8859-1

cp-1251

Стандартне кодування Windows

unicode-escape

Буквенний формат Unicode у Python, виглядає як \uxxxx або \Uxxxxxxxx

Спробуємо використати кодування UTF-8. Присвоємо рядок '\u2615', записаний у Unicode, змінній cup.

cup - це рядок Python у кодуванні Unicode, що містить один символ незалежно від того, скільки байтів може знадобитися для зберігання цього рядка:

>>> cup = '\u2615'
>>> len(cup)
1

Закодуємо цей символ послідовністю байтів:

>>> result = cup.encode('utf-8')
>>> len(result)
3
>>> result
b'\xe2\x98\x95'

Кодування UTF-8 має змінну довжину. У нашому випадку було використано три байта для того, щоб закодувати один символ cup. Функція len() повернула кількість байтів (3), оскільки result є змінної типу bytes.

Можна використовувати і інші кодування, не лише UTF-8, але коли інше кодування не зможе обробити рядок Unicode, з’явиться помилка.

Наприклад, якщо для нашого випадку використати кодування ascii, у нас нічого не вийде, оскільки рядок складається з некоректних символів для ASCII:

>>> result = cup.encode('ascii')
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character '\u2615' in position 0: ordinal not in range(128)

Якщо зустрічається символ, що не входить у кодування ASCII, спостерігається виняток UnicodeEncodeError.

Функція encode() приймає другий аргумент, який допомагає уникнути виникнення помилок, пов’язаних з кодуванням. Цей аргумент може мати значення:

  • 'ignore' - тоді не враховуються ті символи, які не можуть бути закодовані:

>>> result = cup.encode('ascii', 'ignore')
>>> print(result)
b''
  • 'replace' - невідомі символи будуть замінені символами ?:

>>> result = cup.encode('ascii', 'replace')
>>> print(result)
b'?'
  • 'backslashreplace' - невідомі символи будуть замінені на символи, начебто це unicode-escape:

>>> result = cup.encode('ascii', 'backslashreplace')
>>> print(result)
b'\\u2615'
Такий варіант можна використати, якщо потрібно надрукувати керуючі послідовності Unicode.
  • 'xmlcharrefreplace' - створюються рядки символьних сутностей, які можна зустріти на веб-сторінках:

>>> result = cup.encode('ascii', 'xmlcharrefreplace')
>>> print(result)
b'&#9749;'
Декодування

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

Проблема у наступному: ніяка частина байтового рядка не говорить нам про те, яке кодування було використано.

Створимо рядок у форматі Unicode, який має значення 'résumé' (резюме з французької мови) і зберігається у змінній placement. Якщо подивитися на індекс символу «E», можна побачити, що повне ім’я символа E WITH ACUTE, LATIN SMALL LETTER має індекс 00E9:

>>> placement = 'r\u00E9sum\u00E9'
>>> placement
'résumé'
>>> type(placement)
<class 'str'>

Закодуємо рядок placement у форматі UTF-8 і збережемо у змінну placement_bytes, яка є типу bytes:

>>> placement_bytes = placement.encode('utf-8')
>>> placement_bytes
b'r\xc3\xa9sum\xc3\xa9'
>>> len(placement_bytes)
8
>>> type(placement_bytes)
<class 'bytes'>

Зверніть увагу на те, що змінна placement_bytes містить вісім байтів:

  • r, s, u, m - це символи ASCII (один символ - 1 байт, всього 4 байти)

  • \xc3 і \xa9 кодують символ «é» (по 2 байти на кожний символ «é», всього 4 байти)

Тепер декодуємо цей байтовий рядок назад у рядок Unicode:

>>> placement2 = placement_bytes.decode('utf-8')
>>> placement2
'résumé'

У такому перетворенні все пройшло без проблем, оскільки ми закодували і декодували рядок за допомогою кодування UTF-8. Що відбулося б, якби ми вказали декодувати його за допомогою якого-небудь іншого кодування?

>>> placement3 = placement_bytes.decode('ascii')
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128)

Декодер ASCII згенерував виняток, оскільки байтовое значення 0xc3 некоректне у ASCII.

Існують й інші восьмибітні кодування, на зразок ASCII, де значення між 128 (80 в шістнадцятковій системі) і 255 (FF у шістнадцятковій системі) коректні, але не збігаються зі значеннями UTF-8:

>>> placement4 = placement_bytes.decode('latin-1')
>>> placement4
'résumé'
>>> placement5 = placement_bytes.decode('windows-1251')
>>> placement5
'rГ©sumГ©'
>>> placement6 = placement_bytes.decode('windows-1252')
>>> placement6
'résumé'
>>> placement7 = placement_bytes.decode('cp1251')
>>> placement7
'rГ©sumГ©'
Кодування UTF-8 охоплює багато символів, тому варто використовувати його у своєму коді усюди, де це можливо.

11.2. Регулярні вирази

Для текстового пошуку ми часто користуємось відомим сполученням клавіш Ctrl+F. Регулярні вирази дозволяють задавати шаблон для тексту, який хочуть знайти.

Механізм регулярних виразів реалізований у стандартному модулі re, який необхідно імпортувати. Спочатку визначається рядок-шаблон, співпадіння з яким потрібно знайти, а потім рядок-джерело, у якому слід виконати пошук.

Простий приклад використання виглядає так:

результат = re.match(шаблон, джерело)

Функція match() перевіряє, чи починається джерело з шаблону.

Для більш складних перевірок необхідно скомпілювати шаблон, щоб прискорити пошук:

патерн = re.compile(шаблон)
Патерн - шаблон, зразок.

Далі виконується перевірка за допомогою скомпільованого шаблону:

результат = патерн.match(джерело)

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

  • search() - повертає перший збіг, якщо такий є

  • findall() - повертає список усіх збігів, які не перетинаються один з одним, якщо такі є

Чи починається рядок "Thank’s for your help!", що міститься у змінній source, зі слова "Thank’s"? Розглянемо приклад коду:

>>> import re
>>> source = "Thank's for your help!"
>>> find = re.match("Thank's", source)
>>> if find:
...     print(find.group())
...
Thank's
>>> find = re.match("help", source)
>>> if find:
...     print(find.group())
...
>>>

У випадку шаблону help функція match() нічого не повернула, тому що ця функція працює тільки у тому випадку, якщо шаблон знаходиться на початку джерела.

Функція search() шукає шаблон у будь-якому місці джерела:

>>> find = re.search("help", source)
>>> if find:
...     print(find.group())
...
help
>>> find = re.match(".*help", source)
>>> if find:
...     print(find.group())
...
Thank's for your help

Новий шаблон працює таким чином:

  • символ . означає будь-який один символ

  • символ * означає будь-яку кількість символів

У попередніх прикладах здійснювався пошук тільки одного співпадіння. Якщо необхідно дізнатися скілька разів шаблон входить у джерело, використовують функцію findall():

>>> find = re.findall('h', source)
>>> find
['h', 'h']
>>> print('Found:', len(find), 'matches')
Found: 2 matches

Шаблон у вигляді символа h зустрічається у рядку-джерелі "Thank’s for your help!" 4 рази.

Використовуючи рядок-шаблон можна розділити рядок-джерело на список:

>>> find = re.split('h', source)
>>> find
['T', "ank's for your ", 'elp!']

Використовуючи функцію sub() можна виконати заміну у рядку-джерелі, якщо у ньому знайшлось співпадіння з шаблоном:

>>> find = re.sub('h', '?', source)
>>> find
"T?ank's for your ?elp!"

При створенні регулярних виразів крім звичайних символів (.) , (*), (?) використовують спеціальні символи.

Таблиця "Спеціальні символи"
Шаблон Співпадіння

\d

цифровий символ

\D

нецифровий символ

\w

буквенний або цифровий символ або знак підкреслення

\W

будь-який символ, крім буквенного або цифрового символа або знака підкреслення

\s

символи пробілів

\S

символи, крім пробільних

\b

межа слова

\B

не межа слова

Модуль Python string містить заздалегідь певні рядкові константи, які ми можемо використовувати для тестування. Ми скористаємося константою printable, яка містить 100 друкованих символів ASCII, включаючи літери в обох регістрах, цифри, пробіли та знаки пунктуації:

>>> import string
>>> typing_characters = string.printable
>>> len(typing_characters)
100
>>> typing_characters[:50]
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN'
>>> typing_characters[50:]
'OPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'

Які символи рядка typing_characters є цифрами?

>>> re.findall('\d', typing_characters)
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

Які символи рядка typing_characters є цифрами, буквами і знаком підкреслення?

>>> re.findall('\w', typing_characters)
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_']

Які символи рядка typing_characters є символами пропусків?

>>> re.findall('\s', typing_characters)
[' ', '\t', '\n', '\r', '\x0b', '\x0c']

Регулярні вирази не обмежуються символами ASCII. У наступній перевірці використаємо шаблон \w (шукає букви або цифри або знак підкреслення) і додамо наступні символи:

  • п’ять букв ASCII

  • три розділового знака, які не повинні співпасти з шаблоном \w

  • символ Unicode A WITH CIRCUMFLEX, LATIN SMALL LETTER (\u00E2)

  • символ Unicode A WITH BREVE, LATIN SMALL LETTER (\u0103)

>>> search_string = 'email' + '!?,' + '\u00E2' + '\u0103'
>>> re.findall('\w', search_string)
['e', 'm', 'a', 'i', 'l', 'â', 'ă']

Як і очікувалося, цей шаблон знайшов тільки букви.

При створенні регулярних виразів використовують специфікатори шаблонів.

Таблиця "Специфікатори шаблонів"
Шаблон Співпадіння

abc

буквосполучення abc

(expr)

expr

expr1 | expr2

expr1 або expr2

.

будь-який символ, крім \n

ˆ

початок рядка

$

кінець рядка

prev?

нуль або одне включення prev

prev*

нуль або більше включень prev, максимальна кількість

prev*?

нуль або більше включень prev, мінімальна кількість

prev+

одне або більше включень prev, максимальна кількість

prev+?

одне або більше включень prev, мінімальна кількість

prev{m}

m послідовних включень expr

expr{m,n}

від m до n послідовних включень prev, максимальна кількість

expr{m,n}?

від m до n послідовних включень prev, мінімальна кількість

[abc]

a, або b, або c

[^abc]

не (a, або b, або c)

prev(?= next)

prev, якщо за ним слідує next

prev( ? ! next)

prev, якщо за ним не слідує next

(?<=prev ) next

next, якщо перед ним знаходиться prev

(?<! prev) next

next, якщо перед ним не знаходиться prev

Для початку визначимо рядок-джерело для наших експерементів з регулярними виразами (слова Charles Babbage):

>>> source = '''I have been asked twice [members of Parliament]:
"Tell me graciously, Mr. Babbage, what happens if you enter wrong
numbers in the car?" Can we get the right answer? "I can not even
imagine what confusion in the head might lead to such a question.'''

Знайдемо в усьому тексті рядок 'what':

>>> re.findall('what', source)
['what', 'what']

Далі знайдемо в усьому тексті рядки 'what' або 'can':

>>> re.findall('what|can', source)
['what', 'can', 'what']

Знайдемо рядок 'have' на початку тексту:

>>> re.findall('^have', source)
[]

Знайдемо рядок 'I have' на початку тексту:

>>> re.findall('^I have', source)
['I have']

Знайдемо рядок 'such' в кінці тексту:

>>> re.findall('such$', source)
[]

Нарешті, знайдемо рядок 'such a question.$' в кінці тексту:

>>> re.findall('such a question.$', source)
['such a question.']

Символи ^ і $ називаються якорями: за допомогою якоря ^ виконується пошук на початку рядка, а за допомогою якоря $ - у кінці. Поєднання .$ збігається з будь-яким символом в кінці рядка, включаючи точку, тому регулярний вираз спрацював.

Виконаємо пошуку символів o або i, за якими слідує літера f:

>>> re.findall('[oi]f', source)
['of', 'if']

Знайдемо одне або кілька поєднань символів q, u і e:

>>> re.findall('[que]+', source)
['e', 'ee', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'u', 'e', 'e', 'u', 'e', 'e', 'u', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'u', 'e', 'e', 'e', 'u', 'que']

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

>>> re.findall('even\W', source)
['even\n']

Знайдемо поєднання in, за яким слідує поєднання the:

>>> re.findall('in (?=the)', source)
['in ', 'in ']

І нарешті, поєднання if, перед яким знаходиться happens:

>>> re.findall('(?<=happens) if', source)
[' if']

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

Наступний шаблон повинен збігтися з будь-яким словом, яке починається з ask:

>>> re.findall('\bask', source)
[]

Чому цього не сталося? Python використовує спеціальні керуючі послідовності для рядків. Наприклад, \b для рядка означає «повернення на крок», але у мові регулярних виразів ця послідовність означає початок слова.

Завжди розміщуйте символ r перед рядком шаблону регулярного виразу, і керуючі послідовності Python будуть відключені.
>>> re.findall(r'\bask', source)
['ask']

При використанні функцій match() або search() усі збіги можна отримати з об’єкта результату find, викликавши функцію find.group(). Якщо ви укладете шаблон в круглі дужки, збіги будуть збережені в окрему групу і кортеж, що складається з них, буде доступний завдяки виклику find.groups(), як показано тут:

>>> find = re.search(r'(\bI[a-z\s]+)(\basked)', source)
>>> find.group()
'I have been asked'
>>> find.groups()
('I have been ', 'asked')

До окремих груп збігів, збережених в кортежі, можна звертатися, вказавши номер групи:

>>> find.groups()[0]
'I have been '
>>> find.groups()[1]
'asked'
Для створення регулярних виразів можна скористатися онлайн-сервісами: regexr.com, regex101.com, pythex.org, pyregex.com.

11.3. Бінарні дані

В Python 3 з’явилися такі послідовності восьмибітних цілих чисел, які можуть набувати значень від 0 до 255. Вони є двох типів:

  • bytes - незмінний тип, як кортеж байтів

  • bytearray - змінюється, як список байтів

Створимо список з ім’ям blist і у наступному прикладі створимо змінну типу bytes з ім’ям the_bytes і змінну типу bytearray з ім’ям the_byte_array:

>>> blist = [1, 2, 3, 255]
>>> the_bytes = bytes(blist)
>>> the_bytes
b'\x01\x02\x03\xff'
>>> the_byte_array = bytearray(blist)
>>> the_byte_array
bytearray(b'\x01\x02\x03\xff')

Запис значення типу bytes починається з символу b і лапки, за якими слідують шістнадцяткові послідовності, такі, як \x02 або символи ASCII, закінчується конструкція відповідним символом лапки.

Python перетворює шістнадцяткові послідовності або символи ASCII в маленькі цілі числа, але показує байтові значення, які коректно записані з точки зору кодування ASCII:

>>> b'\x61'
b'a'
>>> b'\x01abc\xff'
b'\x01abc\xff'

Кожна із змінних (the_bytes і the_byte_array) може містити результат, що складається з 256 елементів, що мають значення від 0 до 255 (перед нами не символи, а байти):

>>> the_bytes = bytes(range(0, 256))
>>> the_byte_array = bytearray(range(0, 256))
>>> the_bytes
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'

При виведенні на екран вмісту змінної типу bytes або bytearray Python використовує формат \x xx для недрукованих байтів і їх еквіваленти ASCII для друкованих (за винятком деяких керуючих послідовностей, на зразок \n замість \x0a).

Стандартна бібліотека Python містить модуль struct, який обробляє бінарні дані. За допомогою цього модуля ви можете перетворювати бінарні дані у структури даних Python і навпаки.

Як цей модуль працює з даними бінарних файлів? Напишемо невелику програму, яка витягує ширину і висоту зображення із файлу з розширенням PNG - відомого формату зображень.

Національного управління з аеронавтики і дослідження космічного простору (NASA)
Логотип NASA
Специфікація формату PNG передбачає, що ширина і висота зберігаються у перших 24 байтах.

Код цієї програми

import struct (1)
valid_png_header = b'\x89PNG\r\n\x1a\n' (2)
f = open('nasa_logo.png', 'rb') (3)
data = f.read(30) (4)
if data[:8] == valid_png_header: (5)
    width, height = struct.unpack('>LL', data[16:24]) (6)
    print('Valid PNG, width', width, 'height', height)
else:
    print('Not a valid PNG')
f.close()

з таким результатом виконання

Valid PNG, width 175 height 145

робить наступне:

  1. Імпортування модуля struct.

  2. Змінна valid_png_header містить восьмибайтову послідовність, яка визначає початок коректного PNG-файлу.

  3. Відкривання бінарного файлу із зображенням логотипу.

  4. Зчитування у змінну data перших 30 байт файлу зображення.

  5. Перевірка на валідність PNG-файлу.

  6. Значення змінної width витягується з 16-20-го байтів, а змінної height - з байтів 21-24.

    1. >LL - це рядок формату, який вказує функції unpack(), як інтерпретувати вхідні послідовності байтів і перетворювати їх у типи даних Python.

Розглянемо рядок >LL детальніше:

  • символ > означає, що цілі числа зберігаються в форматі big-endian (зворотний порядок байтів)

  • кожен символ L визначає чотирьохбайтне ціле число типу unsigned long

Ви можете перевірити значення кожного чотирибайтового набору безпосередньо:

>>> data[16:20]
b'\x00\x00\x00\xaf'
>>> data[20:24]
b'\x00\x00\x00\x91'

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

>>> 0xaf
175
>>> 0x91
145

Якщо потрібно відправити їх у протилежному напрямку і перетворити дані Python у байти, використовуйте функцію pack() модуля struct:

>>> struct.pack('>L', 175)
b'\x00\x00\x00\xaf'
>>> struct.pack('>L', 145)
b'\x00\x00\x00\x91'

У таблицях нижче показані специфікатори формату і специфікатори порядку байтів для функцій pack() та unpack().

Таблиця "Специфікатори порядку байтів"
Специфікатор Порядок байтів

<

Прямий порядок

>

Зворотний порядок

Специфікатори порядку байтів розташовуються першими у рядку формату.
Таблиця "Специфікатори формату"
Специфікатор Опис Кількість байтів

x

Пропустити байт

1

b

Знаковий байт

1

B

Беззнаковий байт

1

h

Знакове коротке ціле число

2

H

Беззнакове коротке ціле число

2

i

Знакове ціле число

4

I

Беззнакове ціле число

4

l

Знакове довге ціле число

4

L

Беззнакове довге ціле число

4

Q

Беззнакове дуже довге ціле число

8

f

Число з плаваючою точкою

4

d

Число з плаваючою точкою подвійної точності

8

p

Лічильник і символи

1 + count

s

Символи

count

Специфікатори типу слідують за символом, що вказує порядок байтів.

Перед будь-яким специфікатором може слідувати число, яке вказує кількість: запис 4L аналогічний запису LLLL. Ви можете використовувати префікс лічильника замість конструкції >LL:

>>> struct.unpack('>2L', data[16:24])
(175, 145)

Ми використовували зріз data[16:24], щоб отримати безпосередньо байти, які нас цікавлять. Можна додати специфікатор x, щоб пропустити частини, які нас не цікавлять:

>>> struct.unpack('>16x2L6x', data)
(175, 145)

Останній рядок означає:

  • використовувати формат зі зворотним порядком байтів (>)

  • пропустити 16 байт (16x)

  • прочитати 8 байт - два беззнакових довгих цілих числа (2L)

  • пропустити останні 6 байт (6x) із 30 прочитаних

11.4. Перетворення між двійковими даними та ASCII

Стандартний модуль Python binascii містить функції, які дозволяють конвертувати дані у бінарний вигляд і у різні представлення рядків, наприклад, у шістнадцяткове (з основою 16).

Наприклад, виведемо на екран восьмибайтовий заголовок PNG як послідовність шістнадцяткових значень (і навпаки) замість суміші символів ASCII і керуючих послідовностей виду \x xx, які Python використовує для відображення байтових змінних:

>>> import binascii
>>> valid_png_header = b'\x89PNG\r\n\x1a\n'
>>> print(binascii.hexlify(valid_png_header))
b'89504e470d0a1a0a'
>>> print(binascii.unhexlify(b'89504e470d0a1a0a'))
b'\x89PNG\r\n\x1a\n'

11.5. Завдання

11.5.1. Контрольні запитання

  1. Що таке «система кодування»?

  2. Опишіть характеристики форматів ASCII і Unicode.

  3. Що таке UTF-8?

  4. Для чого використовуються регулярні вирази?

  5. Що називають бінарними даними? Які типи у Python призначені для роботи з бінарними даними?

11.5.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

  1. Створіть рядок Unicode з ім’ям unknown і надайте йому значення '\u2653'. Виведіть на екран значення рядка unknown. Знайдіть ім’я Unicode для unknown.

  2. Створіть рядок Unicode з ім’ям unknown2 і надайте йому значення '\U000057A6'. Виведіть на екран значення рядка unknown2. Знайдіть ім’я Unicode для unknown2.

  3. Закодуйте рядок unknown з використанням кодування UTF-8 у змінну типу bytes з ім’ям unknown_bytes. Виведіть на екран значення змінної unknown_bytes.

  4. Використовуючи кодування UTF-8, декодуйте змінну unknown_bytes у рядок unknown_string. Виведіть на екран значення змінної unknown_string. Чи однакові значення мають змінні unknown_string і unknown?

  5. Продовжіть і надрукуйте рядок I’m ..., використовуючи стиль форматування format. Підставте рядки 'hungry', 'in a good mood', 'looking forward to it', 'worried' і 'thirsty' у цей рядок.

  6. Запишіть наступний лист за формою за допомогою стилю форматування format. Збережіть рядок під ім’ям letter. Створіть словник з ім’ям response, що має значення для ключів: 'addressee', 'amount', 'product', sender, 'post', 'institution'. Виведіть на екран значення змінної letter, у яку підставлені значення зі словника response.

Dear Mr. {addressee}:
With reference to our telephone conversation today, I am writing to confirm your order for: {amount} x {product} (Ref. No. 856).
Please contact us again if we can help in any way.

Yours sincerely,
{sender}
{post} of {institution}

11.5.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

  1. Використайте регулярні вирази для пошуку рядків у тексті. Текст, у якому відбуватиметься пошук, можна скопіювати із сайту Project Gutenberg’s і зберегти у файл. Імпортуйте модуль re, щоб використовувати функції регулярних виразів у Python. Вивести на екран усі слова, які починаються з літери «с», чотирилітерні слова, які починаються з літери «с», слова, які закінчуються на букву «r», слова, які містять чотири літери «time» поспіль.

  2. Виведіть інформацію про однопіксельний прозорий GIF-файл. Використайте функцію unhexlify() для того, щоб перетворити шістнадцятковий рядок у змінну типу bytes з ім’ям gif. Байти, що містяться у змінній gif, визначають однопіксельний прозорий GIF-файл. Коректний файл формату GIF починається з рядка GIF89a. Чи є цей файл коректним? Ширина файлу формату GIF є шістнадцятибітним цілим числом з прямим порядком байтів (little-endian, специфікація GIF), яке починається зі зсуву 6 байт. Його висота має такий же розмір і починається зі зсуву 8 байт. Отримайте і виведіть на екран ці значення для змінної gif. Чи рівні вони 1?

'47494638396101000100800000000000ffffff21f90401000000002c000000000100010000020144003b'
  1. Запишіть у текстовий файл з ім’ям validate.txt інформацію про ширину і висоту зображення, витягнуті із GIF-файлу (на сторінці розміри файлу зменшені, відкрийте зображення у новій вкладці браузера для відображення реальних розмірів). Скористайтеся відомостями із попереднього завдання.

файл зображення із анімацією, формат GIF
До завдання: файл зображення із анімацією, формат GIF

12. WWW

У 1989 році британський спеціаліст з інформатики Тім Бернерс-Лі (Tim Berners-Lee) запропонував спосіб поширення інформації всередині CERN і дослідницької спільноти. Свій проект він назвав World Wide Web (Всесвітня павутина) і виділив три основні ідеї, які були покладені в її основу:

  • HTTP (Hypertext Transfer Protocol, протокол передачі гіпертексту) - специфікація для веб-клієнтів і веб-серверів для обміну запитами та відповідями

  • HTML (Hypertext Markup Language, мова розмітки гіпертексту) - формат для представлення інформації

  • URL (Uniform Resource Locator, уніфікований вказівник ресурсу) - спосіб унікально позначити сервер і ресурс на цьому сервері

Всесвітня павутина - це клієнт-серверна система. Веб-клієнт дає запит веб-серверу:

  1. Відкриває з’єднання за протоколом TCP/IP.

  2. Відправляє URL та іншу інформацію за допомогою протоколу HTTP.

  3. Отримує відповідь, формат якої також визначається протоколом HTTP.

  4. Відповідь включає у себе статус запиту і (у випадку, якщо запиит виконано успішно) дані і формат відповіді (наприклад HTML).

Найвідоміший веб-клієнт - це браузер.

12.1. Створення веб-клієнтів

12.1.1. Бібліотека urllib

Python 3 у власній стандартній бібліотеці містить такі модулі для роботи в Інтернеті:

  1. http - керує всіма особливостями клієнт-серверної взаємодії за протоколом HTTP:

    1. client виконує всю роботу на стороні клієнта.

    2. server допомагає написати веб-сервер.

    3. cookies і cookiejar керують cookies.

  2. urllib працює на базі http:

    1. request - обробляє клієнтські запити.

    2. response - обробляє відповіді сервера.

    3. parse - розбиває URL на частини.

Скористаємося модулем urllib стандартної бібліотеки Python, щоб отримати якусь інформацію із сайту.

URL у наступному прикладі повертає код веб-сторінки в Інтернеті у вигляді HTML (разом з CSS і JavaScript):

import urllib.request as ur
url = 'http://openweathermap.org'
conn = ur.urlopen(url)
data = conn.read()
print(conn.status)
print(conn.getheader('Content-Type'))
print(data)

Цей фрагмент коду відкрив з’єднання з віддаленим сервером, створив запит HTTP і отримав HTTP-відповідь. Відповідь містить не лише сам вміст веб-сторінки. Одна з найбільш важливих частин відповіді - це код статусу HTTP: на екран виводиться значення 200. Це означає, що з’єднання пройшло успішно і отримано відповідь:

200
text/html; charset=UTF-8
b'<!DOCTYPE html>\n<html lang=\'en\'>\n...
...
...</body>\n</html>'

Таких кодів статусу HTTP є багато і вони об’єднуються в групи у відповідності з їх першою цифрою:

  • 1xx (інформація) - cервер отримав запит, але має деяку додаткову інформацію для клієнта.

  • 2xx (успіх) - cпрацювало, кожен код успіху, крім 200, повідомляє додаткові деталі.

  • 3xx (перенаправлення) - ресурс був переміщений, тому відповідь повертає кліенту новий URL.

  • 4xx (помилка клієнта) - деякі проблеми на стороні клієнта на зразок помилки 404 (ресурс не знайдений).

  • 5xx (помилка сервера) - код 500 - це загальна помилка.

Формат даних, які відправляє сервер, вказується значенням Content-Type у заголовку відповіді HTTP. Рядок text/html є MIME-типом і означає, що дані до нас прийшли у вигляді HTML. Значення charset=UTF-8 вказує на кодування веб-сторінки.

Щоб дізнатися, які ще заголовки HTTP нам відправив сервер, доповним попередній код:

for key, value in conn.getheaders():
    print(key, value)

Даний цикл буде відбирати заголовки HTTP-запитів і розміщувати у словнику Python. Назва заголовку - ключ словника, значення заголовку - значення словника. Результати виконання програми доповняться наступними:

...
Server openresty/1.9.7.1
Date Mon, 10 Jul 2017 12:21:49 GMT
Content-Type text/html; charset=UTF-8
Transfer-Encoding chunked
Connection close
X-Powered-By PHP/5.6.23
Cache-Control private, must-revalidate
...

12.1.2. Бібілотека requests

Завдання, пов’язані з розробкою веб-клієнтів, можна виконувати і за допомогою сторонніх бібліотек Python. Одна з таких бібліотек називається requests.

Для детальнішого розгляду бібліотеки requests перегляньте її офіційну документацію.

Для початку потрібно встановити бібліотеку requests (якщо вона не встановлена) з вікна терміналу командою:

pip3 install requests

Реалізуємо попередній приклад за допомогою бібліотеки requests:

import requests
url = 'http://openweathermap.org'
r = requests.get(url)
print(r.status_code)
print(r.headers['content-type'])
print(r.encoding)
print(r.text)

Результати будуть аналогічними і відформатованими:

200
text/html; charset=UTF-8
UTF-8
<!DOCTYPE html>
<html lang='en'>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta  http-equiv=Expires content="Tue, Sep 20 2017 15:27:22 GMT">
...
    </body>
</html>

12.2. Веб-додатки на боці сервера

Python добре підходить для написання веб-серверів і програм, що працюють на боці сервера. Це зумовило появу великої кількості веб-фреймворків.

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

Найпопулярнішим Python фреймворком, особливо для великих проектів, є Django.

Для короткого огляду можливостей фреймворків використаємо два мікрофреймворки Python: Bottle і Flask.

Веб-фреймворки і веб-сервери Python, про які йде мова далі, використовують WSGI Web Server Gateway Interface (WSGI) - універсальний API між веб-додатками і веб-серверами.

12.2.1. Bottle

Bottle складається з одного файлу Python, тому його досить легко випробувати і розгорнути. Bottle не є частиною стандартної бібліотеки Python, тому його треба встановити за допомогою наступної команди:

pip3 install bottle

Розглянемо код, який запустить тестовий веб-сервер, який поверне текстовий рядок, коли ваш браузер перейде за URL http://localhost:9999/. Збережіть цей файл як bottle1.py:

from bottle import route, run
@route('/')
def home():
    return "It's my home page!"
run(host='localhost', port=9999)

Bottle використовує декоратор route, щоб зв’язати URL з наступною функцією; у цьому прикладі / (/ - домашня сторінка сайту) обробляється функцією home().

Декоратор - це функція, яка приймає одну функцію у якості аргументу і повертає іншу функцію.

Запустимо цей сценарій сервера за допомогою наступної команди у вікні терміналу:

python bottle1.py

Якщо в браузері набрати адресу http://localhost:9999/, у вікні браузера з’явиться наш текстовий рядок:

Робота вбудованого тестового веб-сервера фреймворка Bootle
Фреймворк Bootle: робота вбудованого тестового веб-сервера
localhost і 127.0.0.1 - це IP-адреси клієнта, які є для протокола TCP синонімами локального комп’ютера, тому вони спрацюють незалежно від того, чи є підключення до Інтернету. 9999 - номер порту, через який здійснюється з’єднання.

Функція run() запускає вбудований тестовий веб-сервер Bottle.

Тепер замість створення у коді тексту для домашньої сторінки, створимо окремий HTML-файл, який називається index.html і містить такий рядок:

My <strong>new</strong> and <em>improved</em> home page!!!

Bottle буде повертати вміст цього файлу, коли запитується домашня сторінка. Збережіть цей сценарій як bottle2.py:

from bottle import route, run, static_file
@route('/')
def main():
    return static_file('index.html', root='.')
run(host='localhost', port=9999)

У виклику функції static_file() ми хочемо отримати файл index.html з каталогу, вказаному в root (у нашому випадку в '.', поточному каталозі). Якщо код попереднього прикладу все ще виконується, то зупиніть сервер, використавши сполучення клавіш Ctrl+C. Після цього запустіть новий сервер:

python bottle2.py

Тепер, кожен раз, коли ви звертаєтеся до сторінки http://localhost:9999/, ви повинні бачити у браузері відформатоване повідомлення:

My new and improved home page!!!

Додамо ще один приклад, який демонструє, як передавати аргументи в URL і використовувати їх. Створіть новий файл і дайте йому назву bottle3.py:

from bottle import route, run, static_file
@route('/')
def home():
    return static_file('index.html', root='.')
@route('/echo/<thing>')
def echo(thing):
    return "Say hello to my little friend: {0:s}!".format(thing)
run(host='localhost', port=9999)

У коді з’явилася нова функція echo(), у яку ми хочемо передавати рядковий аргумент через URL. За це відповідає рядок @route ('/echo/<thing>') у нашому прикладі.

Конструкція <thing> у маршруті @route означає, що все, що знаходиться в URL після /echo/, присвоюється рядковому аргументу thing, який передається функції echo.

Щоб побачити, що відбудеться у цьому випадку, зупиніть сервер, якщо він усе ще працює, і запустіть його з новим кодом:

python bottle3.py

Далі перейдіть у браузері за посиланням http://localhost:9999/echo/Alex. Ви повинні побачити у вікні браузера наступне:

Передача параметра через адресний рядок браузера, фреймворк Bootle
Робота сервера фреймворка Bootle: передача текстового параметра через адресний рядок браузера

Дані приклади працюють, вводячи URL у браузер. Але можна використовувати клієнтські бібліотеки, на зразок requests, щоб вони виконували роботу за вас. Збережіть цей код як bottle_test.py:

import requests
resp = requests.get('http://localhost:9999/echo/Alex')
if resp.status_code == 200 and resp.text == 'Say hello to my little friend: Alex!':
    print('It worked! That almost never happens!')
else:
    print('Argh, got this:', resp.text)

А тепер запустіть цей код у вікні терміналу:

python bottle_test.py

У терміналі має з’явитися повідомлення:

It worked! That almost never happens!

У виклик функції run() можна додати ще такі аргументи:

  • debug = True - створює сторінку налагодження, у разі отримання помилки HTTP

  • reloader = True - оновлює сторінку у браузері, якщо код програми змінився

12.2.2. Flask

Якщо необхідно більше можливостей від роботи фреймворка, можна спорбувати Flask. Flask у використанні майже такий простий, як і Bottle, але він підтримує багато розширень, які можуть бути корисними у професійній веб-розробці, наприклад аутентифікація за допомогою Facebook і інтеграція з базами даних. У цьому фреймворку збалансовані простота використання і багатий набір функцій.

Flask включає у себе бібліотеку package WSGI werkzeug і бібліотеку шаблонів jinja2.

Flask можна встановити за допомогою вікна терміналу:

pip3 install flask

Спробуємо поекспериментувати із фреймворком Flask. У Flask папка за замовчуванням для статичних файлів називається static, і URL для таких файлів теж починається зі /static. Змінимо папку на '.' (поточна папка) і префікс URL на '' (порожній), щоб дозволити адресі / відображати файл index.html.

У функції run() установка параметра debug = True активізує також автоматне овнолення веб-сторінки, тоді як фреймворк Bottle для налагодження і оновлення використовує окремі аргументи. Збережіть цей код у файлі flask1.py:

from flask import Flask
app = Flask(__name__, static_folder='.', static_url_path='')
@app.route('/')
def home():
    return app.send_static_file('index.html')
@app.route('/echo/<thing>')
def echo(thing):
    return "Say hello to my little friend: {0:s}".format(thing)
app.run(port=9999, debug=True)

Далі запустіть сервер з терміналу:

python flask1.py

Протестуйте домашню сторінку, ввівши у адресний рядок браузера наступний URL: http://localhost:9999/. У вікні браузера можна побачити наступне:

My new and improved home page!!!

Далі спробуйте перейти за адресою http://localhost:9999/echo/Mira і у вікні браузера на вас чекає повідомлення:

Say hello to my little friend: Mira

Є ще одна перевага установки параметра debug рівним True при виклику методу run. Якщо у серверному коді генерується виняток, Flask відкриває особливу відформатовану сторінку, яка містить корисні відомості про те, що і де пішло не так. Навіть більше: ви можете вводити команди, щоб побачити значення змінних у програмі сервера.

Не встановлюйте параметр debug = True на виробничих веб-серверах. Це надасть потенційним зловмисникам занадто багато інформації про ваш сервер.

Flask містить jinja2 - велику систему шаблонів. Розглянемо невеликий приклад одночасного використання jinja2 і Flask.

Створіть папку templates і файл flask2.html всередині неї з таким вмістом:

<html>
    <head>
        <title>Flask2 Example</title>
    </head>
    <body>
        Say hello to my little friend: {{ thing }}
    </body>
</html>

Напишемо серверний код, який отримує цей шаблон, створює HTML і заповнює його значенням аргументу thing, який ми передаємо. Збережіть цей файл під ім’ям flask2.py:

from flask import Flask, render_template
app = Flask(__name__, static_folder='.', static_url_path='')
@app.route('/')
def home():
    return app.send_static_file('index.html')
@app.route('/echo/<thing>')
def echo(thing):
    return render_template('flask2.html', thing=thing)
app.run(port=9999, debug=True)

Тепер у браузері перейдіть за цією адресою http://localhost:9999/echo/James і прочитайте наступне повідомлення:

Say hello to my little friend: James

Модифікуємо наш приклад і збережемо його у папці templates під ім’ям flask3.html:

<html>
    <head>
        <title>Flask3 Example</title>
    </head>
    <body>
        Say hello to my little friend: {{ thing }}
		<br>
        Alas, it just destroyed {{ place }}!
    </body>
</html>

Другий аргумент place в URL можна передати різними способами, наприклад, розширивши URL (збережіть файл як flask3a.py):

from flask import Flask, render_template
app = Flask(__name__, static_folder='.', static_url_path='')
@app.route('/')
def home():
    return app.send_static_file('index.html')
@app.route('/echo/<thing>/<place>')
def echo(thing, place):
    return render_template('flask3.html', thing=thing, place=place)
app.run(port=9999, debug=True)

Зупиніть попередній сценарій тестового сервера, якщо він ще працює, і потім запустіть новий:

python flask3a.py

Перейшовши у браузері за новою адресою http://localhost:9999/echo/James/Bond, ви отримаєте:

Say hello to my little friend: James
Alas, it just destroyed Bond!

Аргументи можна передати як параметри команди GET (збережіть файл як flask3b.py):

from flask import Flask, render_template, request
app = Flask(__name__, static_folder='.', static_url_path='')
@app.route('/')
def home():
    return app.send_static_file('index.html')
@app.route('/echo/')
def echo():
    thing = request.args.get('thing')
    place = request.args.get('place')
    return render_template('flask3.html', thing=thing, place=place)
app.run(port=9999, debug=True)

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

python flask3b.py

На цей раз використайте новий URL: http://localhost:9999/echo/?thing=Frodo&place=Isengard. У вікні браузера має з’явитися наступне повідомлення:

Say hello to my little friend: Frodo
Alas, it just destroyed Isengard!

Коли команда GET використовується в URL, будь-які аргументи повинні передаватися у форматі &key1=value1&key2=value2&…​..

Також можна використовувати оператор словника (**), щоб передати декілька аргуменів у шаблон за допомогою одного словника (файл flask3c.py):

from flask import Flask, render_template, request
app = Flask(__name__, static_folder='.', static_url_path='')
@app.route('/')
def home():
    return app.send_static_file('index.html')
@app.route('/echo/')
def echo():
    kwargs = {}
    kwargs['thing'] = request.args.get('thing')
    kwargs['place'] = request.args.get('place')
    return render_template('flask3.html', **kwargs)
app.run(port=9999, debug=True)

**kwargs діє як конструкція thing = thing, place = place. Використовуючи такий словник, можна заощадити трохи часу, якщо вхідних аргументів багато.

12.3. Скрапінг

12.3.1. Бібліотека beautifulsoup

Деякі кроки у процесі отримання інформації (новини, курси валют чи ціни на товар у різних інтернет-магазинах) зі сторінок Інтернету можна автоматизувати.

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

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

Якщо потрібно потужне рішення, що поєднує в собі можливості пошуку і вибірки даних, варто використати фреймворк Scrapy.

Щоб cпробувати Python у скрапінгу, використаємо модуль BeautifulSoup, який не входить в стандартну бібілотеку Python. Для того щоб встановити BeautifulSoup, введіть наступну команду (в кінці назви вказана версія бібліотеки):

pip install beautifulsoup4

Припустимо, із веб-сторінки https://bottlepy.org/docs/dev/ необхідно отримати усі присутні на ній гіперпосилання.

Елемент (тег) мови HTML, який визначає гіперпосилання, - a, а href - атрибут цього тега, який вказує на місце призначення посилання.

Визначимо функцію get_links(), яка автоматизує роботу по «збиранню» гіперпосилань, і основну програму, яка отримує один (як у нашому випадку) або кілька URL як аргументи командного рядка, збережену у файлі з ім’ям scrap_links.py:

def get_links(url):
    import requests
    from bs4 import BeautifulSoup as soup
    result = requests.get(url)
    page = result.text
    doc = soup(page)
    links = [element.get('href') for element in doc.find_all('a')]
    return links
if __name__ == '__main__':
    import sys
    for url in sys.argv[1:]:
        print('Links in', url)
        for num, link in enumerate(get_links(url), start=1):
            print(num, link)
        print()

Запуск програми відбувається у вікні терміналу за допомогою такої команди:

python scrap_links.py https://bottlepy.org/docs/dev/

У програмному коді функції використаний синтаксис включення для списків (рядок links = [element.get('href') for element in doc.find_all('a')]), який можна замінити звичайним синтаксисом Python:

def get_links(url):
    links = []
    import requests
    from bs4 import BeautifulSoup as soup
    result = requests.get(url)
    page = result.text
    doc = soup(page)
    for element in doc.find_all('a'):
        links.append(element.get('href'))
    return links

Перші рядки результатів виконання програми виглядатимуть так:

1 #bottle-python-web-framework
2 http://www.wsgi.org/
3 http://python.org/
4 http://docs.python.org/library/
5 tutorial.html#tutorial-templates
6 http://www.makotemplates.org/
7 http://jinja.pocoo.org/
8 http://www.cheetahtemplate.org/
9 http://pythonpaste.org/
10 https://github.com/william-os4y/fapws3
11 https://github.com/jonashaag/bjoern
12 https://developers.google.com/appengine/
13 http://www.cherrypy.org/
14 http://www.wsgi.org/
...

12.4. Завдання

12.4.1. Контрольні запитання

  1. Для чого використовують бібліотеки urllib і requests?

  2. Об’єкт якого типу повертає функція requests.get()? Яким чином можна отримати доступ до результату роботи цієї функції у вигляді рядкового значення?

  3. Як отримати код статусу HTTP із відповіді на запит модуля requests?

  4. Що таке «скрапінг»? Як можна застосувати бібліотеку beautifulsoup для скрапінгу?

  5. Назвіть фреймворки для Python. У яких випадках необхідно їх використовувати?

12.4.2. Вправи

Виконайте в інтерактивному інтерпретаторі такі завдання:

  1. За допомогою модуля requests виконайте запит до даної сторінки.

  2. Виведіть на екран інформацію про статус запиту.

  3. Виведіть на екран інформацію про кодування сторінки.

12.4.3. Задачі

Напишіть програми у середовищі програмування для розв’язування таких завдань:

Каркас простого сайту з використанням фреймворка Flask.

Встановіть фреймворк. Використайте для роботи сервера за адресою localhost порт 5000. Якщо комп’ютер вже використовує цей порт, використайте інший. Додайте функцію home() для обробки запитів до домашньої сторінки, яка, наприклад, повертає рядок This is a web page project. It works!. Створіть шаблон jinja2, який називається project.html. Шаблон має зберігатися у папці templates і мати наступний вміст:

<html>
<head>
<title>This is a web page project. It works!</title>
<body>
	This will be a {{name}}, where I will post {{videos}} and {{articles}}.
</body>
</html>

Модифікуйте функцію home(), щоб вона використовувала шаблон project.html. Передайте їй три параметра для команди GET: name, videos і articles.

Словник: основні поняття

Програмування

передання комп’ютеру інструкцій, які вказують йому щось зробити. Наприклад, обчислити математичний вираз, змінити текст, здійснити пошук інформації у файлах або виконати обмін даними з іншими комп’ютерами через мережу Інтернет тощо.

Програміст

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

Алгоритм

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

Програма

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

Мова програмування

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

Скрипт (сценарій)

програма, яка автоматизує деяке завдання.

Інтерпретатор

комп’ютерна програма (або набір комп’ютерних програм), що перетворює (інтерпретує) вихідний код, написаний певною мовою програмування, на семантично еквівалентний код в іншій мові програмування, який, як правило, необхідний для виконання програми комп’ютером.

Вихідний код

англ. source code - будь-який набір інструкцій або оголошень, написаних комп’ютерною мовою програмування у формі, що її може прочитати і модифікувати людина.

Тип даних

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

Об’єкт

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

Змінні

є іменами, які посилаються на значення в пам’яті комп’ютера.

Операнд

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

Оператор

виконує дію над операндами.

Функція

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

Список

(list) - послідовність елементів, розташованих у певному порядку. Доступ до елементів здійснюється за індексом. Значення елементів списку можна змінювати.

Кортеж

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

Словник

(dict) - асоціативний масив - послідовність неупорядкованих пар ключ: значення з вимогою унікальності ключів. На відміну від інших послідовностей, доступ до елементів словника здійснюється по ключу, а не за індексом, ключ може бути будь-якого типу, ключ не допускає змін.

Множина

(set) - «контейнер», що містить унікальні елементи у випадковому порядку. Елементом множини може бути будь-який незмінний тип даних: числа, рядки, кортежі.

Розгалуження

конструкція мови програмування, що забезпечує виконання/невиконання певної команди (набору команд) за умови істинності/хибності деякого логічного виразу.

Цикл

будь-яка багатократно виконувана послідовність команд.

Ітерація

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

Ітератор

об’єкт, що перебирає усі елементи послідовності (переходить від одного елемента до іншого).

Включення

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

Генератор

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

Простори імен

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

Модуль

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

Пакет

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

Клас

спеціальна конструкція, яка використовується для групування пов’язаних змінних та функцій.

Регулярні вирази

(від англ. regular expression) - рядок, що описує або збігається з множиною рядків, відповідно до набору спеціальних синтаксичних правил.

Налагоджувач

(англ. debugger) — комп’ютерна програма, яка використовується для тестування і виправлення помилок інших програм.

Псевдокод

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

Додаток A: Завантаження і встановлення Python

A.1. для Windows

До Windows-систем Python не включений за замовчуванням. Для встановлення Python, виконайте наступні дії:

Для операційної системи Windows XP остання підтримувана версія Python 3.4.
  1. З’ясуйте розрядність вашої операційної системи.

  2. Перейдіть на сайт https://www.python.org/downloads/.

  3. Оберіть версію Python.

  4. Завантажте файл з розширенням .exe відповідної розрядності.

  5. Встановіть Python:

    1. відзначте рекомендований параметр Install launcher for all users

    2. не забудьте встановити прапорець Add Python 3.x to PATH (це полегшить правильне налаштування системи)

    3. оберіть варіант налаштування установки Customize installation

    4. вкажіть папку установки C:\PythonX (де X - номер версії)

Встановлення Python 3.6.3 (64-bit) для Windows
Встановлення Python 3.6.3 (64-bit) для Windows

Перевіримо, чи Python успішно був встановлений на комп’ютер. Для цього натисніть сполучення клавіш Win+R на клавіатурі, введіть команду cmd і натисніть OK. У термінальному вікні, що з’явилося, введіть команду python --version і натисніть Enter:

> python --version
Python 3.6.3

Якщо ви отримали схожий результат, то Python відповідної версії успішно встановлений у вашій системі.

A.2. для Linux (Ubuntu)

У Linux-системи Python вже включений за замовчуванням. Перевіримо встановлені версії мови. Натисніть сполучення клавіш Ctrl+Alt+T на клавіатурі для виклику термінального вікна, введіть наступні команди і перегляньте результати їх виконання:

$ python --version
Python 2.7.13

і

$ python3 --version
Python 3.5.3

Якщо ж версія Python 3 не встановлена або необхідно встановити більш нову версію (наприклад, Python 3.6), виконайте в термінальному вікні наступні команди:

$ sudo add-apt-repository ppa:fkrull/deadsnakes
$ sudo apt-get update
$ sudo apt-get install python3.6

Для запуску в термінальному сеансі певної конкретної версії, наприклад, Python 3.6, введіть команду:

$ python3.6

Додаток B: Налаштування середовища програмування

B.1. IDLE

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

Для встановлення IDLE у Linux (Ubuntu) для версії Python 3 слід виконати команди, на зразок:

$ sudo apt-get install idle-python3.6
Для запуску IDLE через термінал Linux (Ubuntu) використовуйте команду, на зразок idle-python3.6.
Інтегроване середовище IDLE
Інтегроване середовище IDLE у Windows

B.2. Notepad++

Завантажте текстовий редактор з офіційного сайту Notepad++, встановіть програму. Виконайте початкові налаштування, переглянувши анімацію.

Налаштування Notepad++ як IDE для Python у Windows
Налаштування Notepad++ як IDE для Python у Windows

Для запуску ваших програм використовуйте команду Плагіни  NppExec  Execute або клавішу F6.

B.3. PyScripter

Завантажте IDE із офіційного сайту PyScripter (оберіть розрядність, що відповідає розрядності інтерпретатора Python), встановіть програму. Виконайте початкові налаштування, переглянувши анімацію.

Налаштування PyScripter для Python у Windows
Налаштування PyScripter для Python у Windows

Для запуску ваших програм використовуйте сполучення клавіш Ctrl+F9.

B.4. Wing IDE 101

Завантажте IDE із офіційного сайту Wing IDE 101, встановіть програму. Виконайте початкові налаштування, переглянувши анімацію.

Налаштування Wing IDE 101 для Python у Windows
Налаштування Wing IDE 101 для Python у Windows

Для запуску програм використовуйте сполучення клавіш Ctrl+Alt+V.

B.5. Geany

Якщо ви використовуєте Windows, завантажте редактор із офіційного сайту Geany і встановіть програму. Перегляньте анімацію про використання Geany.

Використання Geany для Python у Windows
Використання Geany для Python у Windows

Для запуску програм використовуйте клавішу F5.

Якщо ви використовуєте Linux (Ubuntu), відкрийте вікно терміналу сполученням клавіш Ctrl+Alt+T і виконайте послідовно команди для встановлення Geany:

$ sudo add-apt-repository ppa:geany-dev/ppa
$ sudo apt-get update
$ sudo apt-get install geany
$ sudo apt-get install geany geany-plugins

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

$ geany

Перегляньте анімацію про використання Geany.

Використання Geany для Python у Linux (Ubuntu)
Використання Geany для Python у Linux (Ubuntu)

B.6. Sublime Text 3

У випадку використання Windows, завантажте із офіційного сайту Sublime Text 3 і встановіть текстовий редактор.

Для запуску програм, написаних на Python, виберіть Tools  Build System  Python, а потім виконайте Tools  Build або натисніть Ctrl+B. В нижній частині вікна Sublime Text 3 з’явиться панель терміналу із результатом виконання, наприклад, таким:

Hello, Python!
[Finished in 0.1s]

Якщо у системі встановлено кілька версій Python, необхідно налаштувати файл конфігурації. Налаштуємо цей файл конфігурації, наприклад, для версії Python 3.6. У Sublime Text 3 виконайте команду Tools  Build System  New Build System. Видаліть поточний вміст файлу і запишіть наступний код:

{
	"cmd": ["C:\\Python36\\python.exe", "-i", "-u", "$file"],
    "file_regex": "^[ ]File \"(...?)\", line ([0-9]*)",
    "selector": "source.python"
}
Переконайтеся, що вірно вказано шлях до інтерпретатора. В даному випадку для Windows він такий: C:\\Python36\\python.exe, і записується у файлі конфігурації з двома зворотними слешами \.

Збережіть файл з ім’ям Python3 у папці, яку запропонує Sublime Text 3 при виконанні команди Save. Далі залишається обрати Tools  Build System  Python3 для запуску програм на Python.

У випадку використання Linux (Ubuntu), відкрийте вікно терміналу сполученням клавіш Ctrl+Alt+T і виконайте послідовно команди для встановлення Sublime Text 3:

wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add -
sudo apt-get install apt-transport-https
echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list
sudo apt-get update
sudo apt-get install sublime-text

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

subl

Для зміни файла конфігурації (якщо встановлено кілька версій Python), спочатку дізнайтеся повний шлях до інтерпретатора, виконавши команду:

$ type -a python3.6
python3.6 - це /usr/bin/python3.6

Файл конфігурації в цьому випадку матиме вигляд:

{
	"cmd": ["/usr/bin/python3.6", "-u", "$file"],
	"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
	"selector": "source.python"
}
Замініть python3.6 командою, яку ви використовуєте для запуску термінального сеансу інтерпретатора.

B.6.1. Інтерактивна консоль

Для налаштування інтерактивної консолі в Sublime Text 3 завантажте менеджер пакетів Package Control.sublime-package із сайту packagecontrol.io і збережіть у папку Installed Packages програми.

Папка Installed Packages знаходиться на один рівень вгору від папки Preferences  Browse Packages…​.

Виконайте налаштування, переглянувши анімацію.

Інтерактивна консоль у Sublime Text 3
Sublime Text 3 і плагін SublimeREPL: налаштування інтерактивної консолі Sublime Text 3
У файлах конфігурацій Sublime Text 3 для Windows і Linux (Ubuntu) вписуються різні шляхи до інтерпретатора.

Використовуйте такі налаштування Build System у випадку використання плагіна SublimeREPL:

{
	"target": "run_existing_window_command",
	"id": "repl_python_run",
	"file": "config/Python/Main.sublime-menu"
}

B.7. PyCharm

Для встановлення, запуску і налаштування PyCharm використовуйте інструкцію (англ.) на офіційному сайті.

Використання PyCharm для Python у Windows
Використання PyCharm для Python у Windows

Додаток C: Тестування і налагодження

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

Найпростіший спосіб протестувати програми, написані на Python, - додати команди print().

C.1. Контролери коду

Наступним кроком перед створенням справжніх програм для тестування є використання контролерів коду Python. Одним з таких контролерів коду є pylint. Щоб встановити його, виконайте у терміналі команди:

pip3 install pylint

pylint перевіряє на наявність реальних помилок у коді (наприклад, звернення до змінної до виконання присвоєння їй значення) і невідповідність стилю.

Розглянемо просту програму із файлу style1.py, у якій є логічна і стилістична помилки:

a = 1
b = 2
print(a)
print(b)
print(c)

Запустимо її у термінальному вікні командою pylint style1.py:

No config file found, using default configuration
************* Module style1
C:  5, 0: Final newline missing (missing-final-newline)
C:  1, 0: Missing module docstring (missing-docstring)
C:  1, 0: Invalid constant name "a" (invalid-name)
C:  2, 0: Invalid constant name "b" (invalid-name)
E:  5, 6: Undefined variable 'c' (undefined-variable)

------------------------------------
Your code has been rated at -8.00/10

Результати відображають нашу оцінку (10.0 - це максимальний бал). Рядок, який починається з E, вказує на те, що знайдена помилка, а саме, змінній c не було присвоєне значення до її виведення на екран. Виправимо дану помилку, збережемо зміни у новий файл style2.py

a = 1
b = 2
c = 3
print(a)
print(b)
print(c)

і запустимо його у вікні терміналу pylint style2.py:

No config file found, using default configuration
************* Module style2
C:  6, 0: Final newline missing (missing-final-newline)
C:  1, 0: Missing module docstring (missing-docstring)
C:  1, 0: Invalid constant name "a" (invalid-name)
C:  2, 0: Invalid constant name "b" (invalid-name)
C:  3, 0: Invalid constant name "c" (invalid-name)

-----------------------------------
Your code has been rated at 1.67/10

Більше рядків, які починаються на літеру E, немає і бал збільшився з -8.00 до 1.67.

Тепер pylint вимагає рядок документації (короткий текстовий коментар, який описує код, і знаходиться у верхній частині модуля або функції) і вважає, що короткі імена, на зразок a, b і c є не дуже акуратними. Виправимо код таким чином:

"Module docstring goes here"
def func():
    "Function docstring goes here."
    first = 1
    second = 2
    third = 3
    print(first)
    print(second)
    print(third)
func()

Запустимо код командою pylint style3.py і поглянемо на рахунок:

No config file found, using default configuration
************* Module style3
C: 10, 0: Final newline missing (missing-final-newline)

-----------------------------------
Your code has been rated at 8.75/10

Залишилося у кінці коду програми, після виклику функції func() перейти на новий рядок. Запуск оновленого коду програми, збереженого у файлі pylint style4.py, повертає остаточні результати:

No config file found, using default configuration

------------------------------------
Your code has been rated at 10.00/10

C.2. Тестова програма з unittest

Стандартна бібліотека Python містить пакет unittest для тестування програм.

Напишемо модуль, який записує слова з великої літери. Перша версія модуля буде використовувати стандартну рядкову функцию capitalize() і зберігатиметься у файлі cap.py:

def just_do_it(text):
    return text.capitalize()

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

Очікуваний результат називається твердженням (assertion), тому в межах пакету unittest ви перевіряєте результат за допомогою методів, чиї імена починаються зі слова assert, наприклад assertEqual. Збережіть цей сценарій тестування під назвою test_cap.py:

import unittest
import cap
class TestCap(unittest.TestCase):
    def setUp(self):
        pass
    def tearDown(self):
        pass
    def test_one_word(self):
        text = 'duck'
        result = cap.just_do_it(text)
        self.assertEqual(result, 'Duck')
    def test_multiple_words(self):
        text = 'a veritable flock of ducks'
        result = cap.just_do_it(text)
        self.assertEqual(result, 'A Veritable Flock Of Ducks')
if __name__ == '__main__':
    unittest.main()

Перед кожним методом тестування викликається метод setUp(), а після кожного із методів тестування - метод tearDown().

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

Серцем наших тестів є дві функції з іменами test_one_word() і test_multiple_words(). Кожна з них запускає визначену нами функцію just_do_it() з різними вхідними параметрами і перевіряє, чи отриманий очікуваний результат. Запустимо тест.

Якщо результати виконання файлу test_cap.py виводяться у інтерактивний інтерпретатор, використовуйте виклик unittest.main(exit = False).

Команда python test_cap.py у вікні терміналу викликає два наших методи тестування:

F.
======================================================================
FAIL: test_multiple_words (__main__.TestCap)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_cap.py", line 15, in test_multiple_words
    self.assertEqual(result, 'Basket Of Lollipops')
AssertionError: 'Basket of lollipops' != 'Basket Of Lollipops'
- Basket of lollipops
?        ^  ^
+ Basket Of Lollipops
?        ^  ^


----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (failures=1)

Пакет unittest задоволений результатом першої перевірки (test_one_word), але не результатом другої (test_multiple_words). Символи (^) показують, які рядки відрізняються.

Що особливого у прикладі з кількома словами? Після прочитання документації для рядкової функції capitalize(), зрозуміла причина проблеми: функція збільшує лише першу букву першого слова.

Отже, нам потрібна інша функція. Після прочитання тієї ж сторінки документації, ми знайшли функцію title(). Використайємо у нашому модулі cap.py функцію title()

def just_do_it(text):
    return text.title()

і повторимо тести, виконавши команду python test_cap.py у терміналі:

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

Додамо ще один метод у файл test_cap.py

import unittest
import cap
class TestCap(unittest.TestCase):
    ...
    def test_words_with_apostrophes(self):
        text = "that's interesting"
        result = cap.just_do_it(text)
        self.assertEqual(result, "That's Interesting")
if __name__ == '__main__':
    unittest.main(exit = False)

і запустимо наші тести командою python test_cap.py у терміналі:

..F
======================================================================
FAIL: test_words_with_apostrophes (__main__.TestCap)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_cap.py", line 19, in test_words_with_apostrophes
    self.assertEqual(result, "That's Interesting")
AssertionError: "That'S Interesting" != "That's Interesting"
- That'S Interesting
?      ^
+ That's Interesting
?      ^


----------------------------------------------------------------------
Ran 3 tests in 0.016s

FAILED (failures=1)

Наша функція збільшила букву S у конструкції That’s. У документації до функції title() ми виявили, що вона погано працює з апострофами. У самому кінці документації стандартної бібліотеки, що стосується рядків, ми знаходимо ще одного кандидата - допоміжну функцію з ім’ям capwords(). Використоємо її у файлі cap.py

def just_do_it(text):
    from string import capwords
    return capwords(text)

і знову запустимо тести командою python test_cap.py у терміналі:

...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

Усі тести успішно проходять. Але чи усі варіанти пони покривають? Додамо ще один тест у файл test_cap.py:

import unittest
import cap
class TestCap(unittest.TestCase):
	...
    def test_words_with_quotes(self):
        text = "\"I'm looking forward to it\" said the film hero"
        result = cap.just_do_it(text)
        self.assertEqual(result, "\"I'm Looking Forward To It\" Said The Film Hero")
if __name__ == '__main__':
    unittest.main(exit = False)

Запустимо тести командою python test_cap.py у терміналі:

...F
======================================================================
FAIL: test_words_with_quotes (__main__.TestCap)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_cap.py", line 23, in test_words_with_quotes
    self.assertEqual(result, "\"I'm Looking Forward To It\" Said The Film Hero")
AssertionError: '"i\'m Looking Forward To It" Said The Film Hero' != '"I\'m Looking Forward To It" Said The Film Hero'
- "i'm Looking Forward To It" Said The Film Hero
?  ^
+ "I'm Looking Forward To It" Said The Film Hero
?  ^


----------------------------------------------------------------------
Ran 4 tests in 0.000s

FAILED (failures=1)
Ще однією стандартною бібліотекою для тестування коду Python є пакет doctest.

C.3. Налагоджувач pdb

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

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

Більшість IDE для Python містять вбудовані налагоджувачі.

Найпростіший спосіб виконувати налагодження у Python - виконувати код порядково. Також корисно використовувати функцію vars(), яка відображає значення локальних змінних, включаючи аргументи функцій:

>>> def func(*args, **kwargs):
...     print(vars())
...
>>> func(4, 5, 6)
{'kwargs': {}, 'args': (4, 5, 6)}
>>> func('one', 'two', 'three')
{'kwargs': {}, 'args': ('one', 'two', 'three')}

Ці усі прийоми корисні, але не можуть замінити справжній налагоджувач. Використаємо для налагодження коду стандартний налагоджувач Python pdb.

Якщо ви запускаєте програму з параметром -i, у разі її невдалого завершення Python повертає вас у інтерактивний інтерпретатор.

Розглянемо програму з помилкою, яка виникає в залежності від вхідних даних. Зчитаємо файл, який містить назви країн і їх столиць, розділених комою, і виведемо їх на екран у форматі «столиця, країна».

Візьмемо до уваги, що великі літери у словах можуть бути неправильно розставлені, тому нам потрібно виправити це при виведенні на екран. У файлі також можуть бути зайві пропуски, яких також треба позбутися. Нарешті, зчитуюючи файл, ми маємо зупинитися, якщо зустрінемо слово quit (що складається з суміші прописних і малих літер). Так виглядає файл з даними cities1.csv:

France, Paris
venuzuela,caracas
	LithuniA,vilnius
		quit

Складемо алгоритм розв’язання даної задачі. Запишемо кроки алгоритму за допомогою псевдокоду.

Псевдокод виглядає як програма, але є лише способом виразити логіку простою мовою до перетворення його на справжню програму. Python є дуже схожим на псевдокод, тому його не так важко перетворити у робочу програму:

для кожного рядка у текстовому файлі
	зчитати рядок
	видалити пробіли на початку і у кінці рядка
	якщо знайдений рядок "quit" у рядку, записаний у нижньому регістрі
		зупинитися
	розділити країну і столицю символом коми
	видалити пропуски на початку і у кінці
	записати країну і столицю з великої літери
	вивести на екран столицю, кому і країну

Напишемо файл capitals.py:

def process_cities(filename):
    with open(filename, 'rt') as file:
        for line in file:
        line = line.strip()
        if 'quit' in line.lower():
            return
        country, city = line.split(',')
        city = city.strip()
        country = country.strip()
        print(city.title(), country.title(), sep=',')
if __name__ == '__main__':
    import sys
    process_cities(sys.argv[1])

Протестуємо програму у терміналі, використовуючи команду python capitals.py cities1.csv:

Paris,France
Caracas,Venuzuela
Vilnius,Lithunia

Програма пройшла один тест. Використаємо інший файл cities2.csv:

argentina,buenos aires
bolivia,la paz
brazil,brasilia
chile,santiago
colombia,Bogotá
ecuador,quito
falkland islands,stanley
french guiana,cayenne
guyana,georgetown
paraguay,Asuncion
peru,lima
suriname,paramaribo
uruguay,montevideo
venezuela,caracas
quit

Виконавши у терміналі команду python capitals.py cities2.csv, програма завершується після виведення уього 5 рядків, незважаючи на те, що їх у файлі було 15:

Buenos Aires,Argentina
La Paz,Bolivia
Brasilia,Brazil
Santiago,Chile
Bogotгў,Colombia

Ми можемо продовжувати редагувати файл capitals.py, вставляючи print() у тих місцях, де може виникнути помилка, але подивимось, чи зможе нам допомогти налагоджувач.

Для того, щоб використовувати налагоджувач, потрібно імпортувати модуль pdb з командного рядка (у даному випадку, Windows), ввівши -m pdb, наприклад, так:

C:\Python34>python -m pdb capitals.py cities2.csv
> c:\python34\capitals.py(1)<module>()
-> def process_cities(filename):
(Pdb)

Як бачимо, прогарма запустилась і зупинилась на першому рядку. Якщо ввести символ c (від слова continue - «продовжити»), програма буде працювати, поки не завершиться або звичайним способом, або через помилку:

(Pdb) c
Buenos Aires,Argentina
La Paz,Bolivia
Brasilia,Brazil
Santiago,Chile
Bogotгў,Colombia
The program finished and will be restarted
> c:\python34\capitals.py(1)<module>()
-> def process_cities(filename):

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

Введіть s (step - «крок»), щоб пройти за окремими рядками коду. Це дозволить пройти по всіх рядках. Коли ви застосовуєте команду s, ви також входите в усі функції і проходите кожну порядково.

Введіть n (next - «наступний»), щоб йти по кроках, але не заходити всередину функцій: коли ви перебуваєте на рядку, де викликається функція, ця команда виконує всю функцію і ви опиняєтеся на наступному рядку.

Використовуйте s, якщо ви не впевнені у тому, де саме є проблема, а n - якщо впевнені, що деяка функція проблем не викликає.

Використаємо s, щоб почати рухатися від початку програми до функції process_cities():

(Pdb) s
> c:\python34\capitals.py(11)<module>()
-> if __name__ == '__main__':
(Pdb) s
> c:\python34\capitals.py(12)<module>()
-> import sys
(Pdb) s
> c:\python34\capitals.py(13)<module>()
-> process_cities(sys.argv[1])
(Pdb) s
--Call--
> c:\python34\capitals.py(1)process_cities()
-> def process_cities(filename):
(Pdb) s
> c:\python34\capitals.py(2)process_cities()
-> with open(filename, 'rt') as file:

Введіть l (list - «список»), щоб побачити наступні кілька рядків програми:

(Pdb) l
  1     def process_cities(filename):
  2  ->     with open(filename, 'rt') as file:
  3             for line in file:
  4                 line = line.strip()
  5                 if 'quit' in line.lower():
  6                     return
  7                 country, city = line.split(',')
  8                 city = city.strip()
  9                 country = country.strip()
 10                 print(city.title(), country.title(), sep=',')
 11     if __name__ == '__main__':
(Pdb)
Стрілка -> вказує на поточний рядок.

Ми могли б і далі застосовувати команди s або n в надії щось знайти, але давайте використовувати одну з головних особливостей налагоджувача - точки зупинки.

Точка зупинки (breakpoint - скорочено b) зупиняє виконання програми на вказаному рядку.

В даному випадку ми хочемо дізнатися, чому функція process_cities() викликає завершення програми до прочитання усіх введених рядків. Єдине місце, де ми можемо повернутися з функції до прочитання усіх даних, - це рядок 6 (return). Поставимо точку зупинки на рядку 6:

(Pdb) b 6
Breakpoint 1 at c:\python34\capitals.py:6

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

(Pdb) c
Buenos Aires,Argentina
La Paz,Bolivia
Brasilia,Brazil
Santiago,Chile
Bogotгў,Colombia
> c:\python34\capitals.py(6)process_cities()
-> return

Так, програма зупинилася у точці зупинки в рядку 6. Це говорить, що програма хоче завершитися після прочитання країни, яка йде слідом за Колумбією. Виведемо значення змінної line, щоб побачити, що ми тільки що зчитали:

(Pdb) p line
'ecuador,quito'

Столиця називається «quito»? Ми не очікували, що рядок quit стане частиною вхідних даних.

Можна переглянути усі точки зупинки за допомогою команди b:

(Pdb) b
Num Type         Disp Enb   Where
1   breakpoint   keep yes   at c:\python34\capitals.py:6
        breakpoint already hit 1 time

Команда l показує рядки коду, поточний рядок (->) і усі наявні точки зупинки (B). Виклик команди l без аргументів виведе усі рядки, починаючи з точки попереднього виклику команди l, тому включіть у виклик опціональний параметр - стартовий рядок (у нашому прикладі почнемо з 1):

(Pdb) l 1
  1     def process_cities(filename):
  2         with open(filename, 'rt') as file:
  3             for line in file:
  4                 line = line.strip()
  5                 if 'quit' in line.lower():
  6 B->                 return
  7                 country, city = line.split(',')
  8                 city = city.strip()
  9                 country = country.strip()
 10                 print(city.title(), country.title(), sep=',')

Модифікуємо наш тест так, щоб перевірка виконувалась на повне співпадіння з рядком quit без інших символів:

def process_cities(filename):
    with open(filename, 'rt') as file:
        for line in file:
            line = line.strip()
            if 'quit' == line.lower():
                return
            country, city = line.split(',')
            city = city.strip()
            country = country.strip()
            print(city.title(), country.title(), sep=',')
if __name__ == '__main__':
    import sys
    process_cities(sys.argv[1])

Запустимо програму ще раз (python capitals.py cities2.csv):

Buenos Aires,Argentina
La Paz,Bolivia
Brasilia,Brazil
Santiago,Chile
Bogotгў,Colombia
Quito,Ecuador
Stanley,Falkland Islands
Cayenne,French Guiana
Georgetown,Guyana
Asuncion,Paraguay
Lima,Peru
Paramaribo,Suriname
Montevideo,Uruguay
Caracas,Venezuela

C.4. Протоколювання з logging

У якийсь момент вам може знадобитися перейти від використання виразів print() до запису повідомлень у журнал протоколювання.

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

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

Для журналювання можна використовувати модуль стандартної бібліотеки logging.

Модуль logging містить наступні концепції:

  • повідомлення, яке ви хочете зберегти у журнал

  • рівні пріоритету і відповідні функції - debug(), info(), warning(), error() і critical()

  • один або кілька об’єктів протоколювання для основного зв’язку з модулем

  • обробники, які направляють значення у термінал, файл, базу даних

  • засоби форматування вихідних даних

  • фільтри, які приймають рішення в залежності від вхідних даних

Розглянемо найпростіший приклад створення журналу протоколювання. Імпортуємо модуль logging і скористаємося деякими з його функцій:

>>> import logging
>>> logging.debug("What are you interested in?")
>>> logging.info("are there any sporting events on at the moment?")
>>> logging.warning("Are there any excursions?")
WARNING:root:Are there any excursions?
>>> logging.error("Can you recommend a good restaurant?")
ERROR:root:Can you recommend a good restaurant?
>>> logging.critical("We need somewhere to stay!")
CRITICAL:root:We need somewhere to stay!

Виклики debug() і info() не зробили нічого, а два інших вивели на екран рядок РІВЕНЬ:root: перед кожним повідомленням.

Щоб знайти певні повідомлення у журналі, можна виконувати пошук певного значення рівня.

Рівень пріоритету за замовчуванням - WARNING, він буде записаний у журнал, коли ми викличемо першу функцію (logging.debug()). Можна вказати рівень за замовчуванням за допомогою функції basicConfig(). Найнижчий рівень - DEBUG, це дає можливість охопити і вищі рівні:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logging.debug("Сan I book tickets here?")
DEBUG:root:Сan I book tickets here?
>>> logging.info("Where can I hire a car?")
INFO:root:Where can I hire a car?

До цього моменту все виконувалось за допомогою стандартних функцій протоколювання, не створюючи спеціалізований об’єкт.

Кожен об’єкт протоколювання має ім’я. Створимо об’єкт, який називається lunch:

>>> import logging
>>> logging.basicConfig(level='DEBUG')
>>> logger = logging.getLogger('lunch')
>>> logger.debug('Do you know any good places to eat?')
DEBUG:lunch:Do you know any good places to eat?

Якщо ім’я об’єкта протоколювання містить точки, то вони поділяють рівні ієрархії таких об’єктів, кожен з яких потенційно має різні пріоритети. Це означає, що об’єкт з ім’ям recipe вище, ніж об’єкт з ім’ям recipe.ingredient.

До цього моменту повідомлення виводилися лиише на екран, що практично не відрізняється від використання функції print(). Щоб відправити повідомлення у різні місця призначення, використовують обробники.

Найпоширенішим місцем призначення є файл журналу. Відправити повідомлення у файл журналу можна так:

>>> import logging
>>> logging.basicConfig(level='DEBUG', filename='my_log.log')
>>> logger = logging.getLogger('glasses')
>>> logger.debug("Where are my glasses?")
>>> logger.warning("Your glasses on the table.")

Рядки більше не показуються на екрані, замість цього вони потрапляють у файл my_log.log:

DEBUG:glasses:Where are my glasses?
WARNING:glasses:Your glasses on the table.

Виклик функції basicConfig() і передача імені файлу в якості аргументу, створили об’єкт типу FileHandler і зробили його доступним об’єкту протоколювання. Модуль протоколювання містить багато обробників для відправки повідомлень у різні місця, на зразок електронної пошти, веб-серверів, екранів і файлів.

Також можна керувати форматом повідомлень журналу. Якщо у функцію basicConfig() передати відформатований рядок format_string атрибуту format, то можна змінити формат виведення за власним бажанням:

>>> import logging
>>> format_string = '%(asctime)s %(levelname)s %(lineno)s %(message)s'
>>> logging.basicConfig(level='DEBUG', format=format_string)
>>> logger = logging.getLogger('yacht')
>>> logger.error("Where's cabin number 1?")
2017-07-17 11:41:43,319 ERROR 1 Where's cabin number 1?

Модуль logging розпізнав кількість імен змінних у відформатованому рядку format_string: asctime (дата і час як рядок ISO 8601), levelname, lineno (номер рядка) і саме повідомлення у змінній message.