← назад к разделу

PYTHON · SYNTAX AND TYPES Фаза 2. Основы языка: Python

Обязательно

Синтаксис и типы Python: с чего начать

Python · Values · Mutability

Отступы, динамическая типизация, f-strings, изменяемость и деление.

1. Отступы являются синтаксисом

1 / 5

Двоеточие открывает блок, четыре пробела показывают вложенность. Возврат отступа закрывает блок, а не украшает код.

def greet(name): if name: print("Hello") print("Done")indentation defines the block

2. Тип принадлежит значению, не имени

2 / 5

Одно имя может ссылаться на число, затем на строку. Python проверяет операции значения во время выполнения.

x42 · int"42" · str

3. f-string форматирует выражения

3 / 5

f"Total: {total:.2f}" вставляет значение и формат. Это читаемее конкатенации и ручного str.

total = 4990f"Total: {total:.2f}"Total: 4990.00

4. Деление и округление различаются

4 / 5

/ возвращает float, // - целочисленное деление вниз. Для отрицательных чисел это не просто отбрасывание дробной части.

/7 / 2 = 3.5float result//7 // 2 = 3-7 // 2 = -4

5. Мутабельность видна через ссылки

5 / 5

List меняется на месте и делится всеми именами. Tuple, str и int создают новые значения при изменении.

Mutable lista.append(3)b sees the changeImmutable strnew object on changeold reference unchanged

Python привлекает тем, что код на нём читается почти как обычный текст: меньше скобок, меньше церемоний, больше смысла на строку. Но за этой простотой стоят несколько решений языка, которые лучше понять сразу — иначе легко словить неожиданное поведение. Разберём базовый синтаксис и систему типов с самого начала.

Отступы — это блоки кода

В большинстве языков блок кода выделяют фигурными скобками. В Python блок задаётся отступом — обычно четыре пробела. Двоеточие в конце строки открывает новый блок, а возврат на прежний уровень отступа его закрывает.

def greet(name):
    if name:                 # двоеточие открывает блок
        print(f"Привет, {name}")   # этот код внутри if
    else:
        print("Привет, гость")
    print("Готово")          # снова уровень функции, вне if/else

Из этого следует простое правило: отступы — не косметика, а синтаксис. Сместил строку на пробел — изменил смысл программы или получил ошибку. Не смешивай пробелы и табы; общепринятый стиль — четыре пробела.

Переменные и динамическая типизация

Переменную не нужно объявлять заранее и указывать её тип — достаточно присвоить значение. Тип определяется в момент присваивания и может меняться по ходу программы. Это и есть динамическая типизация.

x = 42        # сейчас x — целое число
x = "сорок"   # а теперь та же переменная ссылается на строку

Важно понимать, что переменная в Python — это не «коробка со значением», а имя, привязанное к объекту. Присваивание не копирует значение, а перенаправляет имя на другой объект. Узнать тип текущего значения можно функцией type:

живой пример

n = 42
print(type(n))   # <class 'int'>
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Динамическая типизация удобна для быстрой разработки, но за ней легко не заметить ошибку: переменная незаметно стала не тем, чем вы ожидали. Поэтому в серьёзных проектах добавляют подсказки типов (об этом — в отдельной статье).

Базовые типы

Минимальный набор, с которого всё начинается:

age = 30           # int — целое число произвольной длины
price = 19.99      # float — число с плавающей точкой
name = "Анна"      # str — строка (текст в кавычках)
active = True      # bool — логический тип: True или False
nothing = None     # None — «значения нет»

Несколько деталей, которые экономят время:

  • int не имеет фиксированного предела разрядности — большие числа не переполняются, Python расширяет их автоматически.
  • float — это та же двоичная плавающая точка, что и везде, со всеми её сюрпризами: 0.1 + 0.2 даст 0.30000000000000004, а не ровно 0.3. Поэтому такие числа никогда не сравнивают через == — только с допуском: math.isclose(a, b) из модуля math (при сравнении с нулём ему нужен ещё abs_tol). Для денег используют тип Decimal из модуля decimal.
  • bool на самом деле подвид int: True равен 1, False равен 0. Поэтому True + True вернёт 2 (так делать не стоит, но знать полезно).
  • None — единственное значение типа NoneType, означает «здесь ничего нет». Сравнивают с ним через is: if value is None.

f-строки: подстановка значений в текст

Самый удобный способ собрать строку из значений — f-строка: буква f перед кавычками включает подстановку выражений в фигурных скобках.

живой пример

name = "Анна"
age = 30
price = 19.99
print(f"{name}, возраст {age}")          # Анна, возраст 30
print(f"Через год будет {age + 1}")      # выражение тоже работает
print(f"Цена: {price:.2f}")              # форматирование: 19.99
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Внутри {} можно писать любое выражение, а после двоеточия — спецификатор формата (например, :.2f — два знака после точки). f-строки читаемее и быстрее, чем старая конкатенация через + или метод format.

Операторы

Арифметика выглядит привычно, но есть пара особенностей:

живой пример

print(7 / 2)    # 3.5   — обычное деление всегда даёт float
print(7 // 2)   # 3     — целочисленное деление (отбрасывает дробь)
print(7 % 2)    # 1     — остаток от деления
print(2 ** 10)  # 1024  — возведение в степень
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Сравнения возвращают bool и их можно связывать в цепочку, как в математике:

живой пример

x = 5
print(0 < x < 10)   # True — то же, что 0 < x and x < 10
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Логические операторы пишутся словами: and, or, not. А оператор == сравнивает значения, тогда как is — это тот же самый объект в памяти. Для чисел и строк почти всегда нужен ==; is используют только с None.

Управляющие конструкции

Ветвления и циклы тоже опираются на отступы и двоеточие.

score = 75

if score >= 90:
    grade = "отлично"
elif score >= 60:           # elif — это «else if»
    grade = "нормально"
else:
    grade = "слабо"

Цикл for в Python перебирает элементы последовательности, а не считает индексы. Чтобы пройтись по диапазону чисел, берут range:

живой пример

for i in range(3):          # 0, 1, 2
    print(i)

for letter in "abc":        # строку тоже можно перебирать
    print(letter)
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Цикл while повторяет блок, пока условие истинно. Управляют циклами break (выйти) и continue (перейти к следующей итерации):

живой пример

n = 0
while True:
    n += 1
    if n == 3:
        continue   # пропустить остаток итерации
    if n > 5:
        break      # выйти из цикла
    print(n)       # 1, 2, 4, 5
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Всё в Python — объект

Ключевая идея языка: любое значение — это объект. Число, строка, функция, даже сам тип — у всего есть свой тип, свои методы и идентичность. Это значит, что функцию можно положить в переменную, передать в другую функцию или вернуть из неё.

живой пример

def shout(text):
    return text.upper()

f = shout            # функция — обычный объект, кладём в переменную
print(f("привет"))   # ПРИВЕТ
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Из этого же следует, что у строки или числа есть методы: "abc".upper(), (-5).__abs__(). Единообразие «всё — объект» делает язык предсказуемым: новые сущности ведут себя по тем же правилам, что и встроенные.

Изменяемые и неизменяемые типы

Это одно из самых важных различий, которое стоит усвоить сразу. Объекты бывают неизменяемые (immutable) — их нельзя поменять после создания, и изменяемые (mutable) — их содержимое можно править на месте.

[1, 2, 3] a b = a b.append(4) виден через a один объект

Присваивание не копирует: два имени смотрят на один список, и правка через одно имя видна через другое.

  • Неизменяемые: int, float, bool, str, bytes, tuple, frozenset, None.
  • Изменяемые: list, dict, set.

Неизменяемость кортежа неглубокая: сам кортеж не поменять, а список внутри него — можно. t = (1, [2]); t[1].append(3) сработает и даст (1, [2, 3]).

Что значит «нельзя поменять» для строки? Любая операция над ней создаёт новый объект, а старый остаётся нетронутым:

живой пример

s = "abc"
s2 = s.upper()   # создаётся новая строка "ABC"
print(s)         # abc — исходная строка не изменилась
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

А список меняется на месте, и это легко приводит к неожиданностям, когда на один объект ссылаются два имени:

живой пример

a = [1, 2, 3]
b = a            # b и a — это одно имя одного списка
b.append(4)
print(a)         # [1, 2, 3, 4] — изменился и a, ведь объект один
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Короткая формула: присваивание копирует ссылку, а не объект. Если нужен независимый список, делают копию явно: b = a.copy() или b = list(a).

Отсюда же — частая ловушка новичка: нельзя использовать изменяемый объект как значение по умолчанию у параметра функции (def f(items=[])), потому что этот список создаётся один раз и сохраняется между вызовами. Подробнее об этом — в статье о функциях.

Дополнительно: при первом чтении можно пропустить

Глубже: деление, округление и сравнение, которые удивляютрасширенное

Несколько мест, где Python ведёт себя не так, как привык человек из других языков, и где ошибка не падает, а тихо даёт не то число.

Целочисленное деление округляет вниз, к минус бесконечности, а не к нулю, и остаток берёт знак делителя:

живой пример

print(-7 // 2)   # -4, а не -3
print(-7 % 2)    # 1
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

round округляет к ближайшему чётному, когда дробь ровно половина: round(2.5) даёт 2, round(3.5) даёт 4. Для денег это неприемлемо, поэтому там Decimal и quantize с явным правилом округления:

живой пример

from decimal import Decimal, ROUND_HALF_UP
print(Decimal("2.675").quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))   # 2.68
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Оператор is у маленьких чисел и коротких строк иногда даёт True просто потому, что интерпретатор держит их в одном экземпляре. На это нельзя рассчитывать: значения сравнивают через ==, а is оставляют для None.

С версии 3.10 есть match — разбор значения по образцу, который заменяет лесенку if/elif там, где ветвление идёт по форме данных:

match command.split():
    case ["go", direction]:
        move(direction)
    case ["quit"]:
        stop()
    case _:
        print("не понял")

А с версии 3.12 внутри f-строки можно повторять те же кавычки и писать многострочные выражения: f"{"вложенные" if ok else "кавычки"}" больше не ошибка.

Глубже: регулярные выражения: re и флагирасширенное

Проверить формат, вытащить число из строки, заменить все вхождения по шаблону: для этого есть модуль re. Шаблон пишут сырой строкой r"...", чтобы обратные слеши не пришлось удваивать. Четыре функции закрывают почти всё: re.search находит первое совпадение где угодно в строке, re.match только в её начале, re.findall возвращает все совпадения списком, re.sub заменяет. Группы в круглых скобках вытаскивают части совпадения, а именованные группы (?P<year>\d{4}) делают это читаемым.

живой пример

import re

log = "2026-10-05 ERROR order=42 user=Анна; 2026-10-05 error order=43 user=Иван"
pattern = re.compile(r"(?P<level>error) order=(?P<order>\d+)", re.IGNORECASE)
for m in pattern.finditer(log):
    print(m.group("level"), m.group("order"))  # ERROR 42, error 43
print(re.sub(r"user=\S+", "user=***", log))
Запустить

Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Три дня бесплатно →

Флаг re.IGNORECASE даёт нечувствительность к регистру, re.MULTILINE заставляет ^ и $ работать на каждой строке, re.VERBOSE разрешает пробелы и комментарии внутри длинного шаблона. Шаблон, который используется в цикле или в обработчике, компилируют один раз через re.compile, хотя модуль и сам кеширует последние шаблоны. Две оговорки: match без $ в конце проверяет только начало строки, а не всю её, и шаблоны с вложенными повторениями вроде (a+)+ на неудачном входе работают экспоненциально долго, поэтому на пользовательский ввод их не выпускают.

Коротко

  • Блоки кода задаются отступами (4 пробела) и двоеточием, а не скобками — отступ это синтаксис.
  • Типизация динамическая: тип привязан к значению, а переменная — это имя-ссылка на объект.
  • Базовые типы: int (без предела разрядности), float (двоичный, неточный для дробей), str, bool (подвид int), None.
  • f-строки (f"{value}") — основной способ собирать текст из значений.
  • / всегда даёт float, // — целочисленное деление; == сравнивает значения, is — идентичность (нужен для None).
  • Всё — объект: функции, типы и числа можно хранить в переменных и передавать.
  • Делите типы на неизменяемые (int, str, tuple) и изменяемые (list, dict, set); присваивание копирует ссылку, а не объект.
  • // округляет вниз, round — к ближайшему чётному; для денег Decimal с явным правилом округления.
  • Регулярные выражения: re.search, re.findall, re.sub, сырые строки r"...", флаги IGNORECASE и MULTILINE, компиляция через re.compile для шаблона в цикле.

Что почитать дальше