Шпаргалка по 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.
Ресурсы
- Документация Python — официальная документация
- Python на GitHub — исходный код CPython
Что дальше
- Шпаргалка по NumPy: массивы, линейная алгебра, broadcasting
- Шпаргалка по Pandas: DataFrame, groupby, временные ряды
- Шпаргалка по Matplotlib: графики от простых до публикационных
- Шпаргалка по Seaborn: статистическая визуализация
- Шпаргалка по параллелизму: потоки, процессы, asyncio
- Шпаргалка по FastAPI: REST API, Pydantic, WebSocket
- Шпаргалка по Django: модели, views, ORM, админка
- Шпаргалка по uv: пакетный менеджер на Rust
- Шпаргалка Regex: регулярные выражения
- Шпаргалка по Scikit-learn: машинное обучение на Python
- Data Science инструменты: стек, roadmap, с чего начать
Связанные шпаргалки: NumPy, Pandas, Matplotlib, Seaborn, Параллелизм, FastAPI, Django, uv, Regex, SQL для начинающих, Rust, Go, Си, C++, Алгоритмы