«Питон — язык, который позволяет думать о задаче, а не о языке» — Гвидо ван Россум
Python — интерпретируемый язык программирования общего назначения с акцентом на читаемость кода.
Создан: Гвидо ван Россумом (Guido van Rossum), нидерландским программистом. Первая версия: 1991 год. Назван в честь британского комедийного шоу Monty Python's Flying Circus — не в честь змеи.
В конце 1980-х Гвидо работал над языком ABC — учебным языком с чистым синтаксисом. ABC был хорош для обучения, но плохо интегрировался с реальными системами: не умел работать с файловой системой, не расширялся модулями, не был практичен для реальных задач.
Python создавался как практичный наследник ABC: такой же читаемый, но пригодный для настоящей работы — скриптов, автоматизации, работы с файлами и системой.
- Читаемость как главный принцип — отступы вместо скобок, код читается почти как текст
- Батарейки включены (batteries included) — огромная стандартная библиотека, не нужны сторонние пакеты для базовых задач
- Динамическая типизация — не нужно объявлять типы переменных
- Интерактивность — можно запускать код строка за строкой в REPL
- Кроссплатформенность — один код работает на Windows, macOS, Linux
- Огромная экосистема — pip, PyPI, миллионы библиотек
- Веб — Django, FastAPI, Flask
- Data Science и ML — NumPy, Pandas, TensorFlow, PyTorch
- Автоматизация и скрипты — замена bash для сложных задач
- DevOps — Ansible написан на Python
- Научные вычисления — астрофизика, биоинформатика, физика
- Обучение — самый популярный первый язык
CPython (стандартная реализация Python) написан на языке C.
- C — системный язык близкий к железу. Компилируется прямо в машинный код — максимальная скорость выполнения
- Переносимость — C-компиляторы существуют для всех платформ. Написал интерпретатор на C — он работает везде, где есть C
- Интеграция — Python-код может напрямую вызывать C-библиотеки. Так работают NumPy, OpenCV и другие быстрые библиотеки: интерфейс на Python, вычисления на C
- Зрелость — C существует с 1972 года, крайне стабилен и предсказуем
CPython — не единственная реализация. Есть PyPy (Python на Python, с JIT-компилятором — быстрее CPython), Jython (на Java), IronPython (на C# для .NET). Но CPython — стандарт.
Python — мультипарадигменный язык. Не заставляет следовать одному стилю.
- Процедурное — последовательность функций и инструкций
- Объектно-ориентированное (ООП) — классы, объекты, наследование
- Функциональное — функции как объекты первого класса,
map,filter,lambda, замыкания
# Процедурное
result = 0
for i in range(10):
result += i
# ООП
class Counter:
def __init__(self):
self.value = 0
def increment(self):
self.value += 1
# Функциональное
result = sum(range(10))
squares = list(map(lambda x: x**2, range(10)))Python не заставляет выбирать — можно смешивать стили внутри одного проекта.
Прежде чем понять как работает Python — нужно понять разницу между двумя подходами исполнения кода.
Компиляция — перевод всего исходного кода в машинный код до запуска. Результат — исполняемый файл (.exe, бинарник), который процессор выполняет напрямую.
исходный код (.c, .cpp, .go)
↓
компилятор (один раз, заранее)
↓
машинный код (.exe / бинарник)
↓
процессор выполняет напрямую — без посредников
Примеры: C, C++, Go, Rust.
Плюсы:
- Максимальная скорость — процессор работает с готовым машинным кодом без посредников
- Ошибки типов и синтаксиса обнаруживаются ещё до запуска
Минусы:
- Бинарник привязан к платформе:
windows.exeне запустится на Linux - Цикл «изменил → скомпилировал → запустил» замедляет разработку
Интерпретация — исходный код выполняется строка за строкой прямо во время работы программы. Посредник — интерпретатор — читает строку и сразу её выполняет.
исходный код (.py)
↓
интерпретатор читает строку
↓
выполняет строку
↓
читает следующую строку
↓
...и так до конца файла
Примеры: Python, Ruby, PHP.
Плюсы:
- Один код запускается везде, где установлен интерпретатор
- Быстрая разработка — сохранил, запустил, увидел результат
- Интерактивность — REPL, Jupyter Notebook
Минусы:
- Медленнее компилируемых: каждая строка обрабатывается интерпретатором во время работы
- Ошибки обнаруживаются только в момент выполнения конкретной строки
| Компилируемый | Интерпретируемый | |
|---|---|---|
| Когда переводится в машинный код | До запуска | Во время выполнения |
| Скорость | Быстрее | Медленнее |
| Переносимость | Бинарник под каждую ОС | Один файл — везде |
| Ошибки | На этапе компиляции | В момент выполнения строки |
| Скорость разработки | Медленнее | Быстрее |
| Примеры | C, Go, Rust, C++ | Python, Ruby, PHP |
Python — интерпретируемый, но с нюансом: перед интерпретацией он компилирует код в байткод (
.pyc). Это ускоряет повторные запуски, но байткод всё равно выполняется виртуальной машиной, а не процессором напрямую.
Запуск python script.py — это не просто «читать и выполнять строки». Внутри происходит четыре этапа.
script.py (исходный текст)
↓
1. Лексический анализ → токены
↓
2. Синтаксический анализ → AST (дерево)
↓
3. Компиляция → байткод (.pyc в __pycache__)
↓
4. CPython VM → выполнение → результат
Исходный текст разбивается на токены — минимальные смысловые единицы: ключевые слова, имена переменных, числа, операторы.
x = 42 + 1[NAME 'x'] [OP '='] [NUMBER '42'] [OP '+'] [NUMBER '1']
Здесь проверяется, что символы вообще допустимы в языке.
Токены превращаются в AST (Abstract Syntax Tree) — абстрактное синтаксическое дерево. Иерархическое представление структуры программы.
x = 42 + 1Assign
├── target: Name('x')
└── value: BinOp
├── left: Constant(42)
├── op: Add
└── right: Constant(1)
Здесь проверяется грамматика. x = = 42 — ошибка синтаксиса обнаруживается именно здесь.
AST компилируется в байткод — набор простых инструкций для виртуальной машины Python. Сохраняется в файлы .pyc в папке __pycache__ — при следующем запуске, если файл не изменился, этапы 1–3 пропускаются.
import dis
dis.dis("x = 42 + 1")LOAD_CONST 43 # Python вычислил 42+1=43 заранее (оптимизация)
STORE_NAME x # сохранить результат в переменную x
Байткод — не машинный код. Это инструкции для виртуальной машины CPython.
CPython VM — это C-программа, которая читает байткод и выполняет каждую инструкцию. Реализована как стековая машина: операции берут аргументы из стека и кладут результат обратно.
байткод-инструкция
↓
C-код интерпретатора
↓
машинный код (уже от C-компилятора)
↓
процессор
Именно здесь происходит реальная работа: создаются объекты в памяти, вызываются функции, выполняются вычисления.
На самом нижнем уровне Python оперирует объектами — в Python всё является объектом, включая числа, строки и даже True.
Базовые (атомарные) типы:
42 # int — целое число (без ограничения размера!)
3.14 # float — число с плавающей точкой (64-битный double из C)
True / False # bool — логическое (подкласс int: True == 1, False == 0)
"текст" # str — строка Unicode (не байты)
b"байты" # bytes — байтовая строка
None # NoneType — отсутствие значения, аналог nullКаждое значение в памяти — это C-структура (PyObject), содержащая:
- счётчик ссылок — сколько переменных ссылается на объект (для сборщика мусора)
- указатель на тип объекта
- само значение
// упрощённо, как число 42 живёт в памяти CPython (C-код)
typedef struct {
int ob_refcnt; // счётчик ссылок
PyTypeObject *ob_type; // тип: int, str, float...
} PyObject;
typedef struct {
PyObject ob_base;
long ob_ival; // само число: 42
} PyLongObject;
x = 42в Python — это не 4 байта в памяти как в C. Это полноценный объект с метаданными. Поэтому Python потребляет больше памяти, чем C, но даёт безопасность и гибкость.
Целые числа в Python не переполняются — в отличие от C (int там максимум ~2 млрд). Python автоматически расширяет размер числа.
2 ** 1000 # работает, выведет огромное число — в C переполнилось быОбъекты создаются в куче, но кто освобождает память, когда объект больше не нужен? В CPython — два механизма.
1. Подсчёт ссылок (основной). У каждого объекта есть счётчик ссылок (ob_refcnt): сколько имён/контейнеров на него указывают. Ссылку добавили — счётчик +1, удалили — −1. Как только счётчик дошёл до 0 — объект немедленно уничтожается, память освобождается.
import sys
a = []
sys.getrefcount(a) # → 2 (сама переменная a + временный аргумент функции)
b = a
sys.getrefcount(a) # → 3 — теперь на список ссылаются a и b
del b # −1 → снова 22. Циклический сборщик (дополнительный). Подсчёт ссылок не справляется с циклами: если a ссылается на b, а b на a, их счётчики никогда не дойдут до 0, даже когда извне на них никто не ссылается — утечка памяти. Для этого есть отдельный сборщик (модуль gc), который периодически находит недостижимые циклы и удаляет их.
import gc
class Node: pass
x = Node(); y = Node()
x.ref = y; y.ref = x # цикл: x → y → x
del x, y # извне ссылок нет, но счётчики ещё по 1 (друг на друга)
gc.collect() # → 2 — циклический сборщик нашёл и удалил обаПростыми словами: обычный мусор (счётчик → 0) убирается сразу, зацикленный — периодической уборкой
gc. Поэтому в Python обычно не думают о памяти вручную, но знать про циклы полезно (например, разрывать ссылки или использоватьweakref).
В 2008 году вышел Python 3 — обратно несовместимый с Python 2. Код на Python 2 не запускается на интерпретаторе Python 3 без изменений.
Python 2 официально прекратил поддержку 1 января 2020 года. Сейчас используется только Python 3.
print — функция vs оператор
# Python 2
print "Hello" # оператор, скобки не нужны
# Python 3
print("Hello") # функция, скобки обязательныДеление целых чисел
# Python 2
5 / 2 # → 2 (целочисленное деление по умолчанию — неожиданно!)
5 / 2.0 # → 2.5
# Python 3
5 / 2 # → 2.5 (всегда float)
5 // 2 # → 2 (явное целочисленное деление — оператор //)Строки — байты vs Unicode
# Python 2
"текст" # байтовая строка (bytes) — кодировка зависит от системы
u"текст" # юникодная строка (нужен префикс u)
# Python 3
"текст" # всегда Unicode ← проблем с кодировками стало меньше
b"текст" # байтовая строка (нужен префикс b)range — список vs генератор
# Python 2
range(1000000) # создаёт список из миллиона чисел в памяти сразу — тяжело
# Python 3
range(1000000) # генератор, числа вычисляются по одному — экономит памятьinput — поведение
# Python 2
raw_input("Введи: ") # возвращает строку — безопасно
input("Введи: ") # вычисляет введённое как Python-выражение — опасно!
# Python 3
input("Введи: ") # всегда возвращает строку (как raw_input в Python 2)Аннотации типов — появились в Python 3, в Python 2 нет:
# Python 3
def greet(name: str) -> str:
return f"Привет, {name}"f-строки — появились в Python 3.6:
name = "Андрей"
# Python 2 / ранний Python 3
print("Привет, %s" % name)
print("Привет, {}".format(name))
# Python 3.6+
print(f"Привет, {name}") # f-строка — удобно и читаемо| Различие | Python 2 | Python 3 |
|---|---|---|
оператор print "x" |
функция print("x") |
|
| Деление int | 5/2 → 2 |
5/2 → 2.5 |
| Строки | bytes по умолчанию | Unicode по умолчанию |
| range | список в памяти | генератор |
| input | вычисляет выражение | возвращает строку |
| f-строки | нет | есть (3.6+) |
| Аннотации типов | нет | есть (3.5+) |
| Поддержка | прекращена в 2020 | активна |