Лексичний аналіз (токенізація)
Python — це інтерпретована мова програмування. Для того, щоб виконати програму на Python треба мати інтерпретатор.
Підпрограми інтерпретатора:
- Лексичний аналізатор (лексер) — зчитує текстовий файл із програмою на Python посимвольно та генерує потік лексем (токенів).
- Синтаксичний аналізатор (парсер) — обробляє потік лексем (токенів). На основі їх комбінацій інтерпретатор визначає, які оператори записані в програмі.
Якщо лексер або парсер натрапляють на невідому їм конструкцію, інтерпретатор повідомляє про синтаксичну помилку SyntaxError
із зазначенням місця в коді, де її було виявлено.
Якщо синтаксичних помилок немає, у результаті формується бінарний код, готовий до виконання на Python Virtual Machine.
У цьому розділі:
Інтерпретатор Python, як і більшість інтерпретаторів та компіляторів мов програмування, слідує структурованому конвеєру для виконання коду. Одним з перших і найважливіших етапів цього процесу є лексичний аналіз. Цей етап відповідає за перетворення послідовності символів у послідовність значущих токенів, які може розпізнати парсер.
Лексичний аналіз (також відомий як лексинг або токенізація) — це процес розбиття вихідного коду на токени, які є найменшими одиницями значення в програмі. Ці токени можуть представляти ключові слова, ідентифікатори, оператори, літерали тощо. Лексичний аналізатор (або лексер) виконує цю задачу, скануючи вхідний текст і розпізнаючи шаблони відповідно до визначених правил.
Розглянемо наступний простий оператор Python:
x = 10 + 20
Лексер розбиває його на наступні токени:
x
(ідентифікатор)=
(оператор присвоєння)10
(цілий літерал)+
(оператор додавання)20
(цілий літерал)
Ці токени потім передаються парсеру для подальшого синтаксичного аналізу.
Як працює лексичний аналіз у Python
Інтерпретатор Python включає вбудований лексичний аналізатор, який обробляє вихідний код перед його розбором. Він читає код символ за символом і застосовує попередньо визначені правила для формування токенів.
Процес токенізації в Python:
- Зчитування вхідних даних: інтерпретатор Python зчитує вихідний код з файлу або інтерактивного сеансу.
- Розбиття на токени: токенізатор розбиває код на розпізнавані токени.
- Ігнорування пробілів і коментарів: лексер відкидає пробіли (за винятком відступів, які мають значення в Python) і коментарі (
# Це коментар
). - Обробка відступів: на відміну від багатьох інших мов, Python використовує відступи для визначення блоків коду, що робить рівні відступів частиною потоку токенів.
Логічні та фізичні рядки в Python
Python відрізняє два поняття: фізичні рядки та логічні рядки. Це важливо для розуміння синтаксису мови та правильного форматування коду.
Фізичний рядок — це окремий рядок у вихідному коді програми. Його межі визначаються символом нового рядка. Наприклад, у файлі script.py
такі три рядки є трьома фізичними рядками:
print("Hello")
print("World")
print("!")
Python автоматично розпізнає фізичні рядки за допомогою символів нового рядка (\n
на Unix-подібних системах або \r\n
у Windows).
Логічний рядок — це мінімальна синтаксична одиниця, яку Python інтерпретує як окрему інструкцію. Зазвичай, один фізичний рядок відповідає одному логічному рядку. Наприклад:
x = 10 + 20 # один фізичний рядок = один логічний рядок
Проте Python дозволяє писати один логічний рядок на кілька фізичних, використовуючи явне продовження рядка:
result = (10 + 20 +
30 + 40 +
50 + 60)
У цьому випадку використовуються дужки ()
, і Python автоматично об'єднує три фізичні рядки в один логічний.
Якщо потрібно об'єднати фізичні рядки без використання дужок, списків або інших структур, можна застосувати зворотний слеш \
наприкинці рядку:
result = 10 + 20 + \
30 + 40 + \
50 + 60
Проте використання \
вважається менш зручним, оскільки може ускладнювати читабельність коду.
Зверніть увагу, що це синтаксична помилка, тому що декілька фізичних рядків не обʼєднані в одну логічну:
result = 10 + 20 +
30 + 40 +
50 + 60
Висновки:
- Фізичний рядок — це окремий рядок у файлі коду.
- Логічний рядок — це окремий оператор, яку інтерпретує Python.
- Для поділу логічного рядка на кілька фізичних можна використовувати
()
або\
. - Використання
()
,[]
,{}
для переносу рядків є кращим стилем програмування, ніж\
.
Розуміння різниці між фізичними та логічними рядками допомагає уникати синтаксичних помилок і робити код більш читабельним.
Типи токенів у Python
Лексер Python розпізнає різні типи токенів, серед яких:
- ідентифікатори: імена змінних та функцій;
- ключові слова — це спеціальні ідентифікатори, які зарезервовані та не можуть використовуватись як звичайні ідентифікатори:
if
,else
,def
,class
,return
,True
,False
тощо. - літерали:
- рядки (
"hello"
,'world'
,"""дуже довгий рядок"""
,'''ще один довгий'''
); - цілі числа (integer) у десятичной системі числення (
14
), у двійковій (0b101010111
), у шістнадцятирічній системі числення (0xc001ffff
); - дійсні числа (floating point):
3.14
,10.
,.001
;
- рядки (
- оператори:
+
,-
,*
,/
,=
,==
тощо. - роздільники: дужки
(
,)
, квадратні дужки[
,]
, фігурні дужки{
,}
. - токени відступів і нового рядка використовуються для відстеження структури блоку.
Зверніть увагу, що важливо розрізняти токени:
- імʼя
func1
та рядок"func1"
це різні токени; - оператор
+
та рядок"+"
це різні токени; - ціле число
10
, дійсне число10.
та рядок"10"
це різні літерали;
Коментарі
Коментар починається з символу #
(решітка, hash) і закінчується кінцем фізичного рядка. Коментарі ігноруються синтаксичним аналізатором.
Якщо логічний рядок містить лише непомітні символи: пробіли, табуляції, то він ігнорується.
Непомітні символи (пробіли та табуляції) можуть використовуватися в будь-якій кількості між лексемами (токенами), за винятком початку логічних рядків.
# Це коментар якій займає весь рядок
x = 42 # Це коментар наприкинці рядка
# x = 42 # Цей код буде проігнорований інтерпретатором та не виконається
# Наступний блок коду сортує список за зростанням
numbers = [5, 3, 8, 1]
numbers.sort()
"""
Це багаторядковий коментар
(технічно — рядковий літерал, але використовується для коментування)
"""
def greet(name):
"""Повертає рядок з привітанням"""
return f"Hello, {name}!"
Ідентифікатори
Чому потрібні ідентифікатори? Комп'ютер оперує адресами комірок пам'яті, зберігаючи в них дані та інструкції. Однак робота безпосередньо з адресами незручна для програмістів: запам'ятовувати числові адреси та асоціювати їх із конкретними значеннями складно й неефективно. Ось як би виглядала функція пошуку коренів квадратного рівняння, якщо замість назв були би адреси комірок памʼяті:
def 0x1a2b3c4d(0x9f8e7d6c, 0x5a4b3c2d, 0x1e2f3g4h) -> None:
# Пошук коренів квадратного рівняння виду ax^2 + bx + c = 0
0xa1b2c3d4 = 0x5a4b3c2d * 0x5a4b3c2d - 4 * 0x9f8e7d6c * 0x1e2f3g4h
if 0xa1b2c3d4 < 0:
0x78bd54fd("Рівняння не має дійсних коренів (дискримінант < 0).")
0xf1e2d3c4 = (-0x5a4b3c2d + 0xa1b2c3d4**0.5) / (2 * 0x9f8e7d6c)
0xb4c3d2e1 = (-0x5a4b3c2d - 0xa1b2c3d4**0.5) / (2 * 0x9f8e7d6c)
0x78bd54fd("Корені квадратного рівняння:", 0xf1e2d3c4, 0xb4c3d2e1)
Щоб зробити код більш читабельним і зручним, використовуються ідентифікатори — імена, що прив’язуються до об’єктів у пам’яті. Завдяки цьому ми можемо працювати з осмисленими іменами, а не з довгими числовими адресами.
Ідентифікатор у Python — це ім'я, яке використовується для позначення змінних, функцій, класів, модулів тощо. Він має відповідати певним правилам:
- Ідентифікатор може містити лише літери
a-z
,A-Z
, цифри0-9
і знак підкреслення_
. - Він не може починатися з цифри.
- Python розрізняє великі та малі літери (наприклад,
name
іName
— різні ідентифікатори). - Деякі зарезервовані слова (наприклад,
def
,class
,if
) не можуть бути ідентифікаторами.
Приклади правильних ідентифікаторів:
user_name = "Alice"
age = 30
_total = 100
def process_data():
pass
Приклади неправильних ідентифікаторів:
2name = "Bob" # Починається з цифри
class = "student" # Використання зарезервованого слова class
user-name = "Charlie" # Містить недозволений символ "-"
В Python усі змінні — це іменовані посилання на об'єкти в пам'яті. Наприклад:
x = 10
y = x
Тут обидві змінні x
і y
посилаються на один і той самий об'єкт у пам'яті, що містить значення 10
.
Ідентифікатори спрощують роботу програмістів, роблячи код зрозумілим та зручним. Дотримання правил їх найменування дозволяє уникнути помилок і покращує читабельність програмного коду.
Ключові слова
Ключові (зарезервовані) слова — це спеціальні ідентифікатори, які мають фіксоване значення та не можуть використовуватися як імена змінних, функцій або будь-яких інших об'єктів. Вони визначають синтаксис і логіку мови програмування.
Ключові слова потрібні для того, щоб забезпечити структуру мови та зробити її зрозумілою й логічною для програмістів. Вони допомагають визначати типи даних, управляти потоком виконання програми, працювати з класами, функціями та обробкою помилок.
У Python є наступні зарезервовані слова:
False await else import pass
None break except in raise
True class finally is return
and continue for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
Ключові слова | Значення та контекст використання |
---|---|
True , False |
Логічні значення "істина" (True ) та "хиба" (False ) у булевій логіці. |
None |
Представляє відсутність значення або "нічого". |
if , else , elif |
Організація умовних конструкцій. |
for , in , break , continue , while , pass |
Організації циклів. |
def , return , lambda |
Визначення функцій та повернення значень. |
try , except , finally , raise |
Обробки виключень. |
class , global , nonlocal |
Роботи з класами та змінними у різних областях видимості. |
and , or , not , is |
Логічні оператори. |
import , from , as |
Імпорт модулів та призначення псевдонімів. |
with |
Роботи з ресурсами. |
async , await |
Асинхронне програмування. |
del |
Видалення об'єктів. |
assert |
Перевірка тверджень. |
yield |
Створення генераторів. |
Важливо: Python розрізняє великі та малі літери, тому використовувати ключове слово False
для імені змінної не можна, а false
можна. Це не буде синтаксичною помилкою. Але робити так не рекомендується.