Кладовочка

О разном, чтобы было под рукой онлайн

Шпаргалка по Python: от основ до профи

Python — интерпретируемый язык с динамической типизацией. Шпаргалка охватывает всё: от базового синтаксиса до продвинутых тем. Примеры идут от простого к сложному — бери и используй.

Основы синтаксиса и типы данных

Переменные и константы

name = "Маша"
age = 25
PI = 3.14159          # константа по соглашению

x, y, z = 1, 2, 3     # множественное присваивание
a = b = c = 0          # каскадное

Базовые типы

# int, float, str, bool, None
42                     # int
3.14                   # float
1e-5                   # float (научная нотация)
"Hello"                # str
"""Многострочная"""    # str (тройные кавычки)
True, False            # bool
None                   # отсутствие значения

Операторы

# Арифметика: + - * / // % **
10 / 3    # 3.333...    (обычное деление)
10 // 3   # 3           (целочисленное)
10 % 3    # 1           (остаток)
2 ** 10   # 1024        (степень)

# Сравнение: == != < > <= >=
# Логика: and or not
# Принадлежность: in, not in
# Идентичность: is, is not

Форматирование строк

name = "Мир"
f"Привет, {name}!"                    # f-строки (Python 3.6+)
"Привет, {}!".format(name)            # format
"Привет, %s!" % name                  # %-форматирование (старое)

# f-строки с выражениями
f"{42:05d}"       # '00042' — zero-padded
f"{3.14159:.2f}"  # '3.14' — 2 знака после запятой
f"{255:x}"        # 'ff' — hex
f"{255:b}"        # '11111111' — binary
f"{1000000:,}"    # '1,000,000' — с разделителями тысяч

Преобразование типов

int("42")          # "42" → 42
float("3.14")      # "3.14" → 3.14
str(42)            # 42 → "42"
bool(0)            # False (0, "", [], {}, None — всё False)
list("abc")        # ['a', 'b', 'c']
tuple([1, 2, 3])   # (1, 2, 3)
set([1, 1, 2, 3])  # {1, 2, 3}

Коллекции

Списки (list) — изменяемые, упорядоченные

fruits = ["яблоко", "банан", "апельсин"]
numbers = list(range(1, 6))        # [1, 2, 3, 4, 5]

fruits.append("груша")             # добавить в конец
fruits.insert(1, "киви")           # вставить по индексу
fruits.remove("банан")             # удалить по значению
item = fruits.pop()                # удалить и вернуть последний
item = fruits.pop(0)               # удалить и вернуть по индексу
fruits.sort()                      # сортировка на месте
fruits.sort(reverse=True)          # по убыванию
fruits.reverse()                   # обратный порядок
idx = fruits.index("апельсин")     # найти индекс
count = fruits.count("яблоко")     # подсчёт вхождений
fruits.clear()                     # очистить
fruits.extend(["слива", "вишня"])  # расширить

# Срезы: [начало:конец:шаг]
fruits[1:3]     # элементы с индексом 1 по 2
fruits[:2]      # первые 2
fruits[::2]     # каждый второй
fruits[::-1]    # обратный список
fruits[-3:]     # последние 3

# List comprehensions
squares = [x**2 for x in range(10)]                     # квадраты 0..9
evens = [x for x in range(20) if x % 2 == 0]           # чётные
pairs = [(x, y) for x in range(3) for y in range(3)]   # декартово произведение

Кортежи (tuple) — неизменяемые, упорядоченные

point = (10, 20)
single = (42,)          # кортеж из одного элемента — запятая обязательна!

x, y = point            # распаковка
first, *rest, last = (1, 2, 3, 4, 5)  # first=1, rest=[2,3,4], last=5

# Именованные кортежи
from collections import namedtuple
Person = namedtuple('Person', ['name', 'age', 'city'])
p = Person('Анна', 30, 'Москва')
p.name, p.age           # доступ по имени
p._fields               # ('name', 'age', 'city')
p._asdict()             # в словарь
p._replace(age=31)      # новый кортеж с изменённым полем

Словари (dict) — изменяемые, ключ→значение

person = {"name": "Анна", "age": 30}
d = dict(name="Анна", age=30)          # альтернативное создание

person["email"] = "anna@mail.ru"       # добавить/изменить
age = person.get("age", 0)             # получить с умолчанием
person.setdefault("role", "user")      # получить или установить
person.pop("age")                      # удалить и вернуть
person.update({"job": "teacher"})      # обновить несколькими
del person["job"]                      # удалить

# Итерация
for key in person: pass                # по ключам
for key, value in person.items(): pass # по парам
for value in person.values(): pass     # по значениям

# Dict comprehensions
squares = {x: x**2 for x in range(5)}  # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# Слияние словарей (Python 3.9+)
merged = d1 | d2                       # приоритет у d2 при одинаковых ключах
d1 |= d2                               # обновление на месте

Множества (set) — изменяемые, уникальные, неупорядоченные

nums = {1, 2, 3, 4, 5}
letters = set("hello")             # {'h', 'e', 'l', 'o'}

nums.add(6)                        # добавить
nums.remove(3)                     # удалить (ошибка если нет)
nums.discard(10)                   # удалить (без ошибки)
nums.pop()                         # удалить и вернуть случайный
nums.clear()                       # очистить

# Операции над множествами
a = {1, 2, 3}; b = {2, 3, 4}
a | b     # {1, 2, 3, 4} — объединение
a & b     # {2, 3}       — пересечение
a - b     # {1}          — разность
a ^ b     # {1, 4}       — симметрическая разность
a <= b    # a — подмножество b?
a >= b    # a — надмножество b?

# frozenset — неизменяемое множество (можно использовать как ключ dict)
frozen = frozenset([1, 2, 3])
d = {frozen: "value"}              # ✅ можно
# d = {{1, 2}: "v"}                # ❌ обычный set нельзя

Дополнительные типы из collections

from collections import defaultdict, Counter, deque, OrderedDict, ChainMap

# defaultdict — словарь со значением по умолчанию
dd = defaultdict(int)           # 0 для несуществующих ключей
dd['key'] += 1                  # не требует проверки на существование
dd = defaultdict(list)          # пустой список
groups[category].append(item)

# Counter — частотный словарь
c = Counter('abracadabra')
c.most_common(3)                # [('a', 5), ('b', 2), ('r', 2)]
c1 + c2     # сложение счётчиков
c1 - c2     # вычитание
c1 & c2     # min
c1 | c2     # max

# deque — двусторонняя очередь (O(1) с обоих концов)
d = deque([1, 2, 3], maxlen=3)  # фиксированный размер
d.appendleft(0)                 # [0, 1, 2] — 3 вытеснен
d.append(4)                     # [1, 2, 4]
d.rotate(1)                     # циклический сдвиг

# ChainMap — цепочка словарей (поиск по очереди)
defaults = {'color': 'red', 'size': 'M'}
user = {'color': 'blue'}
chain = ChainMap(user, defaults)
chain['color']    # 'blue' (из user)
chain['size']     # 'M' (из defaults)

Управление потоком

# if/elif/else
if score >= 90:
    grade = 'A'
elif score >= 80:
    grade = 'B'
else:
    grade = 'C'

# Тернарный оператор
x = "чётное" if n % 2 == 0 else "нечётное"

# for ... in
for i, item in enumerate(items, start=1):   # с индексом
    print(f"{i}: {item}")

for a, b in zip(list1, list2):              # параллельно
    print(a, b)

# while
while condition:
    ...

# break / continue / pass / else
for x in range(10):
    if x == 5: break        # выход из цикла
    if x % 2 == 0: continue # пропустить итерацию
else:
    print("Цикл завершился без break")  # else выполняеься только без break

# match/case (Python 3.10+)
match status:
    case 200 | 201 | 204:
        print("Успех")
    case 400:
        print("Ошибка клиента")
    case _:
        print("Неизвестно")

Работа с датой и временем

from datetime import date, time, datetime, timedelta

today = date.today()                       # 2026-07-18
now = datetime.now()                       # 2026-07-18 14:30:00.123456
specific = datetime(2024, 7, 15, 14, 30)
delta = timedelta(days=5, hours=3)

# Форматирование
now.strftime("%Y-%m-%d %H:%M:%S")          # '2026-07-18 14:30:00'
now.strftime("%d.%m.%Y")                   # '18.07.2026'
datetime.strptime("2024-07-15", "%Y-%m-%d")  # строка → datetime

# Коды форматирования
# %Y — год (4 цифры)    %m — месяц (01-12)     %d — день (01-31)
# %H — час (00-23)      %M — минута (00-59)    %S — секунда (00-59)
# %A — день недели       %B — месяц словом
# %a — день нед. сокр.  %b — месяц сокр.

# Дата через неделю
next_week = today + timedelta(days=7)
diff = datetime.now() - specific          # timedelta

# Временные зоны (Python 3.9+)
from zoneinfo import ZoneInfo
moscow = datetime.now(ZoneInfo("Europe/Moscow"))

Модуль time — системное время и задержки

import time

time.time()           # текущее время в секундах с эпохи Unix (float)
time.monotonic()      # монотонное время (не зависит от перевода часов)
time.sleep(0.5)       # приостановка на 0.5 секунды
time.perf_counter()   # самое точное время для замеров производительности
time.localtime()      # локальное время в виде struct_time
time.gmtime()         # UTC время в виде struct_time

# Замер времени выполнения
t0 = time.perf_counter()
result = slow_function()
elapsed = time.perf_counter() - t0

Модуль calendar — работа с календарём

import calendar

calendar.month(2026, 7)          # календарь на июль 2026 (строка)
calendar.isleap(2024)            # True — високосный год
calendar.weekday(2026, 7, 18)    # день недели (0=Пн, 6=Вс) → 5 = суббота
calendar.monthrange(2026, 7)     # (день недели первого дня, число дней)

Работа с файлами

# Чтение
with open('file.txt', 'r', encoding='utf-8') as f:
    content = f.read()               # весь файл
    lines = f.readlines()            # список строк
    for line in f:                   # построчно (не загружает всё в память)
        print(line.strip())

# Запись
with open('file.txt', 'w', encoding='utf-8') as f:
    f.write("Привет!\n")
    f.writelines(["строка 1\n", "строка 2\n"])

# Режимы: r (чтение), w (запись), a (добавление), x (создание)
#         rb/wb/ab (бинарный), r+/w+/a+ (чтение+запись)

# CSV
import csv
with open('data.csv', 'r', encoding='utf-8') as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row['name'], row['age'])

# JSON
import json
with open('data.json', 'r', encoding='utf-8') as f:
    data = json.load(f)
json.dump(data, f, indent=2, ensure_ascii=False)
json_string = json.dumps(data, ensure_ascii=False)

# pathlib — современная работа с путями
from pathlib import Path
p = Path('folder') / 'file.txt'
p.exists()
p.is_file()
p.parent              # 'folder'
p.name                # 'file.txt'
p.suffix              # '.txt'
p.stem                # 'file'
p.read_text()         # весь файл строкой
p.write_text("data")  # запись строки

# os.path — старый способ (все ещё встречается)
import os
os.path.join('folder', 'file.txt')
os.path.exists(path)
os.path.dirname(path)
os.path.basename(path)
os.path.getsize(path)   # размер файла в байтах

# ZIP-архивы
import zipfile
with zipfile.ZipFile('archive.zip', 'r') as z:
    z.extractall('extracted')         # распаковать всё
    print(z.namelist())               # список файлов в архиве

with zipfile.ZipFile('new.zip', 'w') as z:
    z.write('file.txt')               # добавить файл в архив

# Pickle — сериализация Python-объектов
import pickle
data = {"name": "Анна", "scores": [5, 4, 5]}

with open('data.pkl', 'wb') as f:     # бинарная запись
    pickle.dump(data, f)

with open('data.pkl', 'rb') as f:     # бинарное чтение
    loaded = pickle.load(f)           # {"name": "Анна", ...}

Функции

# Базовая функция
def greet(name, greeting="Привет"):
    """Возвращает приветствие."""          # docstring
    return f"{greeting}, {name}!"

# Аргументы: позиционные, именованные, *args, **kwargs
def func(a, b, *args, c=10, **kwargs):
    print(a, b)          # позиционные
    print(args)          # кортеж лишних позиционных
    print(c)             # keyword-only (после *)
    print(kwargs)        # словарь лишних именованных

func(1, 2, 3, 4, c=20, d=30)  # a=1, b=2, args=(3,4), c=20, kwargs={'d':30}

# / и * — разделители типов аргументов
def f(pos_only, /, pos_or_kw, *, kw_only): ...
#       позиционный    позиц.или именов.  только именованный

# lambda — анонимные функции
sorted(items, key=lambda x: x['age'])
map(lambda x: x**2, numbers)
filter(lambda x: x > 0, numbers)
reduce(lambda a, b: a + b, numbers)  # из functools

# Замыкания — функция, которая «помнит» переменные из внешней области
def make_counter(start=0):
    def counter():
        nonlocal start                # nonlocal — меняем переменную внешней функции
        start += 1
        return start
    return counter

count = make_counter(5)
count()  # 6
count()  # 7

# Фабрика функций через замыкание
def make_multiplier(factor):
    return lambda x: x * factor

double = make_multiplier(2)
triple = make_multiplier(3)
double(5)    # 10
triple(5)    # 15

# partial — частичное применение (заранее фиксируем часть аргументов)
from functools import partial

def power(base, exp):
    return base ** exp

square = partial(power, exp=2)      # всегда возводит в квадрат
cube = partial(power, exp=3)        # всегда возводит в куб
square(5)    # 25

# singledispatch — перегрузка функции по типу первого аргумента
from functools import singledispatch

@singledispatch
def format_value(value):
    return f"Неизвестный тип: {type(value)}"

@format_value.register(str)
def _(value):
    return f"Строка: '{value}'"

@format_value.register(int)
def _(value):
    return f"Число: {value} (hex: {value:#x})"

format_value("hello")   # "Строка: 'hello'"
format_value(42)         # "Число: 42 (hex: 0x2a)"

Итераторы и генераторы

# Итератор — объект с __iter__ и __next__
class Counter:
    def __init__(self, limit):
        self.limit = limit
        self.value = 0
    def __iter__(self): return self
    def __next__(self):
        if self.value >= self.limit: raise StopIteration
        self.value += 1
        return self.value

# Генератор — функция с yield (ленивая)
def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

for num in fibonacci(10):   # числа не хранятся в памяти
    print(num)

# Генераторные выражения (ленивый list comprehension)
squares = (x**2 for x in range(10**9))  # не занимает память!
next(squares)  # 0
next(squares)  # 1

# itertools — комбинаторные генераторы
from itertools import (
    count, cycle, repeat,              # бесконечные
    accumulate, chain, compress,       # обработка
    product, permutations, combinations,  # комбинаторные
    groupby, islice, takewhile          # фильтрация/группировка
)

# Примеры
list(product('AB', '12'))             # [('A','1'),('A','2'),('B','1'),('B','2')]
list(permutations('ABC', 2))          # все перестановки по 2
list(combinations('ABC', 2))          # все сочетания по 2
list(accumulate([1,2,3,4]))           # [1, 3, 6, 10] — префиксные суммы

# Генератор: send, throw, close — двусторонняя связь
def accumulator():
    total = 0
    while True:
        value = yield total             # yield возвращает total, принимает value
        if value is None:
            break
        total += value

acc = accumulator()
next(acc)             # запускаем генератор → возвращает 0
acc.send(10)          # отправляем 10 → total=10, yield 10
acc.send(5)           # отправляем 5 → total=15, yield 15
acc.close()           # остановить генератор (бросает GeneratorExit)

# yield from — делегирование другому генератору/итератору
def flatten(nested):
    for item in nested:
        if isinstance(item, list):
            yield from flatten(item)   # делегируем вложенной структуре
        else:
            yield item

list(flatten([1, [2, [3, 4]], 5]))  # [1, 2, 3, 4, 5]

# Бесконечный генератор + islice
from itertools import islice
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

list(islice(fibonacci(), 10))   # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# groupby — группировка по ключу (данные должны быть отсортированы)
data = [('A', 1), ('A', 2), ('B', 3), ('B', 4)]
for key, group in groupby(data, key=lambda x: x[0]):
    print(key, list(group))
    # A [('A', 1), ('A', 2)]
    # B [('B', 3), ('B', 4)]

Декораторы

# Простой декоратор — функция, принимающая функцию
def timer(func):
    import time
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__}: {time.time() - start:.4f}с")
        return result
    return wrapper

@timer
def slow_function():
    ...

# Декоратор с параметрами
def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Привет, {name}")

# Сохранение метаданных
from functools import wraps
def my_decorator(func):
    @wraps(func)                  # копирует __name__, __doc__ в wrapper
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

# Полезные декораторы из functools
from functools import lru_cache, cached_property

@lru_cache(maxsize=128)           # мемоизация
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

# Декоратор класса — принимает и возвращает класс
def add_method(cls):
    def hello(self):
        return f"Привет от {self.__class__.__name__}"
    cls.hello = hello
    return cls

@add_method
class MyClass:
    pass

obj = MyClass()
obj.hello()   # "Привет от MyClass"

# Нанизывание (stacking) декораторов — порядок важен!
@timer
@lru_cache
@repeat(2)
def process(): ...
# process = timer(lru_cache(repeat(2)(process)))
# Выполняется: repeat(2) → lru_cache → timer

# cached_property — свойство с кешированием (ленивое вычисление один раз)
class DataProcessor:
    @cached_property
    def expensive_data(self):
        print("Вычисляю...")                  # вызовется только один раз
        return sum(i*i for i in range(10**7))

Классы и ООП

Базовый класс — атрибуты, методы, инкапсуляция

class Animal:
    species = "Неизвестно"              # атрибут класса (общий для всех экземпляров)

    def __init__(self, name, age):      # конструктор
        self.name = name                # атрибут экземпляра
        self._age = age                 # protected (по соглашению, не enforced)
        self.__secret = "hidden"        # name mangling → _Animal__secret

    def speak(self):                    # метод экземпляра
        return f"{self.name} издаёт звук"

    @classmethod                        # метод класса (cls = сам класс)
    def from_dict(cls, data):
        return cls(data['name'], data['age'])

    @staticmethod                       # статический метод (нет self/cls)
    def is_adult(age):
        return age >= 18

    @property                           # геттер — доступ как к атрибуту без ()
    def age(self):
        return self._age

    @age.setter                         # сеттер — валидация при присваивании
    def age(self, value):
        if value < 0:
            raise ValueError("Возраст не может быть отрицательным")
        self._age = value

    @age.deleter                        # делитер — вызывается при del obj.age
    def age(self):
        print("Удаляем возраст")
        del self._age

    def __str__(self):                  # для print() и str()
        return f"{self.name}, {self._age} лет"
    def __repr__(self):                 # для repr() и отладки (однозначное!)
        return f"Animal(name='{self.name}', age={self._age})"

# Использование
a = Animal("Барсик", 5)
a.age = 6                              # сеттер
Animal.from_dict({"name": "Мурка", "age": 3})  # classmethod

Наследование, MRO и super()

class Dog(Animal):
    species = "Собака"

    def __init__(self, name, age, breed):
        super().__init__(name, age)     # вызов родительского __init__
        self.breed = breed

    def speak(self):                    # переопределение метода
        return f"{self.name} гавкает!"

# Множественное наследование и MRO (Method Resolution Order)
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
D.__mro__   # D → B → C → A → object

# super() в множественном наследовании — идёт по MRO
class Logger:
    def __init__(self):
        super().__init__()              # в MRO дальше идёт не object, а другой класс!
        print("Logger init")

class DB:
    def __init__(self):
        super().__init__()
        print("DB init")

class App(Logger, DB):                  # MRO: App → Logger → DB → object
    pass

App()   # выведет: DB init\nLogger init  (super идёт по MRO!)

ABC — абстрактные классы

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self): ...                 # подклассы обязаны реализовать

    @abstractmethod
    def perimeter(self): ...

    def describe(self):                 # обычный метод — наследуется
        return f"Площадь: {self.area()}, периметр: {self.perimeter()}"

class Circle(Shape):
    def __init__(self, r): self.r = r
    def area(self): return 3.14 * self.r ** 2
    def perimeter(self): return 2 * 3.14 * self.r

# Shape() — TypeError: Can't instantiate abstract class
c = Circle(5)   # OK

Dataclasses — автоматические init, repr, eq

from dataclasses import dataclass, field
from typing import List

@dataclass
class Point:
    x: float
    y: float
    label: str = ""                     # значение по умолчанию

    def distance_to(self, other):
        return ((self.x - other.x)**2 + (self.y - other.y)**2)**0.5

@dataclass(order=True)                  # + __lt__, __le__, __gt__, __ge__
class Person:
    sort_index: int = field(init=False, repr=False)
    name: str = ""
    age: int = 0

    def __post_init__(self):            # вызывается после __init__
        self.sort_index = self.age

@dataclass(frozen=True)                 # неизменяемый (read-only)
class Config:
    host: str
    port: int = 8080
    # config.port = 9000  # ❌ FrozenInstanceError

Миксины — переиспользуемое поведение через множественное наследование

class SerializeMixin:
    """Добавляет to_dict и to_json любому классу"""
    def to_dict(self):
        return {k: v for k, v in self.__dict__.items() if not k.startswith('_')}

    def to_json(self):
        import json
        return json.dumps(self.to_dict(), ensure_ascii=False)

class TimestampMixin:
    """Добавляет created_at и updated_at"""
    from datetime import datetime

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.created_at = self.datetime.now()
        self.updated_at = self.created_at

class User(TimestampMixin, SerializeMixin):
    def __init__(self, name, email):
        super().__init__()
        self.name = name
        self.email = email

u = User("Анна", "anna@mail.ru")
u.to_dict()   # {'name': 'Анна', 'email': 'anna@mail.ru', 'created_at': ..., 'updated_at': ...}

__slots__ — экономия памяти

class PointWithSlots:
    __slots__ = ('x', 'y')              # фиксированный набор атрибутов
    # НЕТ __dict__! Экономия 30-50% памяти на экземпляр.

    def __init__(self, x, y):
        self.x = x
        self.y = y

p = PointWithSlots(1, 2)
# p.z = 3  # ❌ AttributeError — нельзя добавить новый атрибут

__init_subclass__ — хук при создании наследников

class PluginBase:
    registry = {}

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        name = kwargs.get('name', cls.__name__)
        PluginBase.registry[name] = cls

class JSONPlugin(PluginBase, name='json'):
    pass

class XMLPlugin(PluginBase, name='xml'):
    pass

PluginBase.registry  # {'json': JSONPlugin, 'xml': XMLPlugin}

Магические методы (dunder) — полный список

# Создание/удаление
# __init__(self, ...)    — конструктор
# __new__(cls, ...)      — создание объекта (до __init__)
# __del__(self)           — деструктор (редко нужен)

# Строковое представление
# __str__(self)           — для print(), str()
# __repr__(self)          — однозначное, для repr() и отладки

# Контейнеры
# __len__(self)           — len(obj)
# __getitem__(self, key)  — obj[key]
# __setitem__(self, key, v) — obj[key] = v
# __contains__(self, item)— item in obj

# Итерация
# __iter__(self)          — iter(obj), запуск цикла for
# __next__(self)          — next(obj), следующий элемент

# Контекстный менеджер
# __enter__(self)         — вход в with
# __exit__(self, exc, val, tb) — выход из with

# Операторы сравнения
# __eq__ / __ne__ (==, !=)
# __lt__ / __le__ / __gt__ / __ge__ (<, <=, >, >=)

# Арифметические операторы
# __add__ (+)  __sub__ (-)  __mul__ (*)  __truediv__ (/)
# __floordiv__ (//)  __mod__ (%)  __pow__ (**)
# __radd__ / __rmul__ — обратные (когда левый операнд не поддерживает)

# Вызываемый объект
# __call__(self, ...)     — obj()

class Multiplier:
    def __init__(self, factor):
        self.factor = factor
    def __call__(self, x):
        return x * self.factor

double = Multiplier(2)
double(5)   # 10
double(7)   # 14

Дескрипторы — контроль доступа к атрибутам

class Validated:
    """Дескриптор: валидация значения при присваивании"""
    def __init__(self, min_val=None, max_val=None):
        self.min_val = min_val
        self.max_val = max_val
        self.name = None

    def __set_name__(self, owner, name):  # Python 3.6+
        self.name = f"_{name}"            # имя для хранения в __dict__

    def __get__(self, obj, objtype=None):
        if obj is None: return self
        return obj.__dict__.get(self.name)

    def __set__(self, obj, value):
        if self.min_val is not None and value < self.min_val:
            raise ValueError(f"{self.name} должен быть >= {self.min_val}")
        if self.max_val is not None and value > self.max_val:
            raise ValueError(f"{self.name} должен быть <= {self.max_val}")
        obj.__dict__[self.name] = value

class Product:
    price = Validated(min_val=0)
    quantity = Validated(min_val=0, max_val=1000)

p = Product()
p.price = 100    # OK
# p.price = -10  # ❌ ValueError

Метаклассы (кратко)

# Метакласс — «класс класса». Определяет, как создаются классы.
# По умолчанию метакласс — type.

class AutoReprMeta(type):
    def __new__(mcs, name, bases, namespace):
        # Автоматически добавляем __repr__ в класс
        if '__repr__' not in namespace:
            def __repr__(self):
                attrs = ', '.join(f'{k}={v!r}' for k, v in self.__dict__.items())
                return f'{name}({attrs})'
            namespace['__repr__'] = __repr__
        return super().__new__(mcs, name, bases, namespace)

class Point(metaclass=AutoReprMeta):
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(1, 2)
print(p)  # Point(x=1, y=2) — автоматически!

Память и копирование

# Изменяемые vs неизменяемые
# Неизменяемые: int, float, str, tuple, frozenset, bytes
# Изменяемые: list, dict, set, bytearray

a = [1, 2, 3]
b = a               # b — та же ссылка
a.append(4)         # меняет и a, и b (один объект)
print(b)            # [1, 2, 3, 4]

# Копирование
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original)       # копирует контейнер, но не вложенные
deep = copy.deepcopy(original)      # рекурсивное копирование

original[0][0] = 99
print(shallow)  # [[99, 2], [3, 4]] — вложенный список общий!
print(deep)     # [[1, 2], [3, 4]]  — полностью независим

# Копирование встроенных коллекций
new_list = old_list.copy()
new_list = old_list[:]
new_dict = old_dict.copy()
new_set = old_set.copy()

# Проверка идентичности
id(obj)         # уникальный id объекта
a is b          # тот же объект?
a is None       # правильно
a == b          # одинаковые значения?

# Кеширование малых целых чисел (-5 до 256)
a = 256
b = 256
a is b           # True — Python кеширует числа от -5 до 256

# Подсчёт ссылок
import sys
a = [1, 2, 3]
sys.getrefcount(a)   # количество ссылок на объект (включая временную!)

# Слабые ссылки (не мешают сборщику мусора удалить объект)
import weakref
a = {"data": "heavy"}
ref = weakref.ref(a)  # слабая ссылка
ref()                  # {"data": "heavy"} — пока объект жив
del a
ref()                  # None — объект удалён

# WeakValueDictionary — кеш, который не мешает GC
cache = weakref.WeakValueDictionary()
cache["key"] = MyHeavyObject()
# если больше нигде нет ссылки на MyHeavyObject — GC удалит и из кеша

Обработка исключений

try:
    result = 10 / 0
except ZeroDivisionError:
    print("Деление на ноль!")
except (TypeError, ValueError) as e:
    print(f"Ошибка типа или значения: {e}")
except Exception as e:
    print(f"Неизвестная ошибка: {e}")
else:
    print("Всё хорошо, исключений не было")    # выполняется только без исключений
finally:
    print("Выполнится всегда")                  # очистка ресурсов

# Пользовательские исключения
class ValidationError(Exception):
    def __init__(self, message, code=None):
        super().__init__(message)
        self.code = code

raise ValidationError("Неверный формат", code=400)

# Контекстные менеджеры
class ManagedFile:
    def __init__(self, path, mode='r'):
        self.path = path; self.mode = mode
    def __enter__(self):
        self.file = open(self.path, self.mode)
        return self.file
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()
        return False  # не подавлять исключения

with ManagedFile('data.txt') as f:
    content = f.read()

# EAFP против LBYL
# EAFP: "проще попросить прощения, чем разрешения"
try:
    value = data['key']
except KeyError:
    value = default

# LBYL: "смотри, прежде чем прыгнуть"
if 'key' in data:
    value = data['key']

# Assert — проверка инвариантов (отключается с -O флагом)
assert len(data) > 0, "Данные не должны быть пустыми"
# __debug__ по умолчанию True, отключается: python -O script.py

# Exception chaining — цепочка исключений
try:
    process_file("data.csv")
except FileNotFoundError as e:
    raise ValueError("Не удалось обработать файл") from e
    # ValueError c __cause__ = FileNotFoundError — полный контекст!

# Подавление контекста
try:
    ...
except ValueError:
    raise RuntimeError("Что-то пошло не так") from None  # без причины

Регулярные выражения (re)

import re

text = "Email: alice@ex.com, phone: +7 (999) 123-45-67"

# Основные функции
re.search(r'\d+', text)          # первый match или None
re.match(r'Email', text)         # match с начала строки
re.findall(r'\d+', text)         # список всех совпадений
re.finditer(r'\d+', text)        # итератор match-объектов
re.sub(r'\d', 'X', text)         # замена
re.split(r',\s*', text)          # разбиение

# Match-объект
m = re.search(r'(\w+)@(\w+\.\w+)', text)
m.group()       # 'alice@ex.com'  — всё совпадение
m.group(1)      # 'alice'         — первая группа
m.groups()      # ('alice', 'ex.com')
m.span()        # (7, 21) — позиция совпадения
m.start(), m.end()

# Синтаксис паттернов
# . — любой символ кроме \n
# \d — цифра          \D — не цифра
# \w — буква/цифра/_  \W — не буква/цифра/_
# \s — пробел          \S — не пробел
# ^ — начало строки    $ — конец строки
# \b — граница слова

# Квантификаторы
# * — 0+    + — 1+    ? — 0 или 1
# {n} — ровно n    {n,} — n+    {n,m} — n..m
# *? +? ?? {n,m}? — нежадные версии

# Группы
# (...) — группа с захватом
# (?:...) — группа без захвата
# (?P<name>...) — именованная группа
# (?=...) — позитивный lookahead
# (?!...) — негативный lookahead
# (?<=...) — позитивный lookbehind
# (?<!...) — негативный lookbehind

# Флаги
re.IGNORECASE  # re.I — игнорировать регистр
re.MULTILINE   # re.M — ^ и $ работают для каждой строки
re.DOTALL      # re.S — . включает \n
re.VERBOSE     # re.X — игнорировать пробелы и комментарии в паттерне

# Пример: email валидация
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
re.match(pattern, email)

# Пример: извлечение доменов
re.findall(r'@(\w+\.\w+)', text)

# Компиляция для многократного использования
email_re = re.compile(r'[\w.+-]+@[\w-]+\.[\w.-]+')
email_re.findall(big_text)

# re.subn — замена + количество замен
new_text, count = re.subn(r'\s+', ' ', 'a   b  c')
# new_text = 'a b c', count = 3

# Динамическая замена через функцию
def mask_phone(match):
    phone = match.group(0)
    return phone[:4] + '***' + phone[-3:]  # скрываем середину номера

re.sub(r'\+7\s?\d{3}\s?\d{3}[\-\s]?\d{2}[\-\s]?\d{2}',
       mask_phone, 'Звоните: +7 999 123-45-67')
# 'Звоните: +7 9***5-67'

# Verbose — сложный паттерн с комментариями (для телефона)
phone_re = re.compile(r'''
    (\+7|8)?            # код страны (опционально)
    \s*                 # пробелы
    \(?                 # открывающая скобка (опционально)
    (\d{3})             # код города (3 цифры)
    \)?                 # закрывающая скобка
    \s*                 # пробелы
    (\d{3})             # первые 3 цифры номера
    [-\s]?              # разделитель (опционально)
    (\d{2})             # следующие 2 цифры
    [-\s]?              # разделитель (опционально)
    (\d{2})             # последние 2 цифры
''', re.VERBOSE)

Полезные модули стандартной библиотеки

# typing — аннотации типов
from typing import List, Dict, Tuple, Optional, Union, Any, Callable

def process(items: List[int], config: Optional[Dict[str, Any]] = None) -> Tuple[int, int]:
    ...

# enum — перечисления
from enum import Enum, auto

class Status(Enum):
    ACTIVE = auto()     # авто-нумерация
    INACTIVE = auto()
    BANNED = "banned"   # или своё значение

# random — генерация случайных чисел
import random
random.random()              # [0.0, 1.0)
random.randint(1, 10)        # int [1, 10]
random.choice(seq)           # случайный элемент
random.sample(pop, k)        # k уникальных
random.shuffle(seq)          # перемешать на месте
random.seed(42)              # воспроизводимость

Виртуальное окружение и зависимости

python -m venv .venv              # создать окружение
source .venv/bin/activate         # активировать (Linux/Mac)
.venv\Scripts\activate            # активировать (Windows)
deactivate                        # выйти

pip install package               # установить
pip install package==1.2.3        # конкретная версия
pip install -r requirements.txt   # из файла
pip freeze > requirements.txt     # сохранить зависимости
pip list                          # список установленного
pip show package                  # инфо о пакете

Python 3.14: free-threading и другие новшества

Free-threading — CPython без GIL

Начиная с Python 3.13 (экспериментально) и 3.14 (официально), доступна free-threaded сборка — CPython без Global Interpreter Lock. Потоки исполняются по-настоящему параллельно на всех ядрах CPU.

# Установка: официальный инсталлятор → галочка "free-threaded binaries"
# Или сборка из исходников:
./configure --disable-gil && make -j$(nproc) && sudo make install

# Запуск free-threaded версии
python3.14t script.py

# Проверка
python -VV                    # "... (free-threading build)"
import sysconfig, sys

# Проверить, free-threaded ли сборка
is_free = sysconfig.get_config_var("Py_GIL_DISABLED") == 1
print(f"Free-threaded: {is_free}, GIL enabled: {sys._is_gil_enabled()}")

# Управление GIL в рантайме
# PYTHON_GIL=1 python3.14t — принудительно включить GIL
# python3.14t -X gil=0       — принудительно выключить

Ключевые изменения:

  • Производительность: до 4x на CPU-bound многопоточном коде без изменений
  • Оверхед: +1-8% на однонопоточном коде (зависит от платформы)
  • Память: +30-50% на некоторых структурах (None: 32 байта вместо 16)
  • Совместимость: C-расширения без поддержки free-threading автоматически включают GIL обратно
  • Трекер совместимости: py-free-threading.github.io
# Что нужно поправить в старом коде:
# 1. Явные блокировки вместо надежды на GIL
counter = 0
lock = threading.Lock()       # добавляем lock
with lock:
    counter += 1              # ✅ теперь точно потокобезопасно

# 2. Динамический выбор Executor
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
if sysconfig.get_config_var("Py_GIL_DISABLED"):
    Executor = ThreadPoolExecutor   # потоки теперь параллельны!
else:
    Executor = ProcessPoolExecutor

Экспериментальный JIT-компилятор (Python 3.14)

python3.14 -X jit=1 script.py    # включить JIT
# Пока экспериментально, ускоряет вычислительные участки кода

Подробнее — в шпаргалке по параллелизму в Python.

Ресурсы

Что дальше


Связанные шпаргалки: NumPy, Pandas, Matplotlib, Seaborn, Параллелизм, FastAPI, Django, uv, Regex, SQL для начинающих, Rust, Go, Си, C++, Алгоритмы