Обзор стандартной библиотеки
Девиз Python — batteries included: многое, за чем в других языках идут в сторонние пакеты, уже есть в стандартной библиотеке. Знать, что там есть, важнее, чем помнить все сигнатуры.
collections
from collections import Counter, defaultdict, deque, OrderedDict, ChainMap
# Counter — подсчёт
votes = Counter(["за", "против", "за", "за", "воздержался"])
print(votes, votes.most_common(1), votes["нет такого"])
print(Counter("hello") + Counter("world")) # арифметика счётчиков
# defaultdict — значение по умолчанию для нового ключа
by_dept = defaultdict(list)
for name, dept in [("Анна", "IT"), ("Боря", "HR"), ("Вера", "IT")]:
by_dept[dept].append(name)
print(dict(by_dept))
# deque — двусторонняя очередь: O(1) с обоих концов
q = deque([1, 2, 3], maxlen=4)
q.appendleft(0); q.append(4) # 0 вытеснен из-за maxlen
print(q)
q.rotate(1) # сдвиг по кругу вправо
print(q, "→ popleft:", q.popleft(), "→", q)
# ChainMap — поиск по нескольким словарям по очереди
defaults = {"theme": "light", "lang": "ru"}
user = {"theme": "dark"}
settings = ChainMap(user, defaults)
print(settings["theme"], settings["lang"])
# OrderedDict — move_to_end пригодится для LRU-кеша
od = OrderedDict(a=1, b=2, c=3)
od.move_to_end("a")
print(list(od), od.popitem(last=False))
heapq и bisect
import heapq, bisect
tasks = [(3, "почта"), (1, "пожар"), (2, "отчёт")]
heapq.heapify(tasks) # минимальная куча за O(n)
heapq.heappush(tasks, (0, "критично"))
print(heapq.heappop(tasks), heapq.heappop(tasks)) # всегда минимальный — O(log n)
print(heapq.nlargest(2, [5, 1, 9, 3, 7]), heapq.nsmallest(2, [5, 1, 9, 3, 7]))
scores = [10, 20, 30, 40] # отсортированный список
print(bisect.bisect_left(scores, 25)) # позиция для вставки — двоичный поиск
bisect.insort(scores, 25)
print(scores)
grades = "FDCBA"
print([grades[bisect.bisect([60, 70, 80, 90], s)] for s in [55, 73, 99]])
Подробно кучи и двоичный поиск разберём в модуле алгоритмов.
Числа: math, statistics, decimal, fractions
import math, statistics
from decimal import Decimal, ROUND_HALF_UP
from fractions import Fraction
print(math.sqrt(2), math.isclose(0.1 + 0.2, 0.3), math.gcd(12, 18), math.lcm(4, 6))
print(math.ceil(2.1), math.floor(-2.1), math.comb(5, 2), math.factorial(5), math.inf > 10**100)
data = [2, 4, 4, 4, 5, 5, 7, 9]
print(statistics.mean(data), statistics.median(data), statistics.mode(data), statistics.stdev(data))
print(0.1 + 0.2, Decimal("0.1") + Decimal("0.2")) # Decimal — точная десятичная арифметика
print(Decimal("2.675").quantize(Decimal("0.01"), rounding=ROUND_HALF_UP), round(2.675, 2))
print(Fraction(1, 3) + Fraction(1, 6))
Деньги — не float
Decimal("0.1") из строки. Decimal(0.1) из float унаследует ошибку двоичного представления. В базах данных для денег — тип NUMERIC/DECIMAL или целые копейки.
random и secrets
import random, secrets
random.seed(42) # воспроизводимость — для тестов и симуляций
print(random.randint(1, 6), random.random(), random.choice("абв"))
cards = list(range(10))
random.shuffle(cards)
print(cards, random.sample(range(100), 3), random.choices("ab", weights=[9, 1], k=5))
print(secrets.token_hex(16)) # криптостойкие случайные значения
print(secrets.token_urlsafe(16), secrets.randbelow(100))
random предсказуем (вихрь Мерсенна) — для паролей, токенов и ключей только secrets.
Строки и текст
import string, textwrap
print(string.ascii_lowercase, string.digits, string.punctuation)
text = "Python — высокоуровневый язык программирования общего назначения с динамической типизацией."
print(textwrap.fill(text, width=40))
print(textwrap.shorten(text, width=40, placeholder="…"))
print(textwrap.dedent("""
отступ
сохраняется относительно
""").strip())
Идентификаторы, хеши, кодирование
import uuid, hashlib, hmac, base64
print(uuid.uuid4()) # случайный UUID
print(hashlib.sha256(b"hello").hexdigest()[:32], "...")
print(hashlib.md5(b"hello").hexdigest()) # md5 — только для контрольных сумм, не для паролей!
signature = hmac.new(b"secret-key", b"payload", hashlib.sha256).hexdigest()
print(hmac.compare_digest(signature, signature)) # сравнение без утечки по времени
encoded = base64.b64encode("Привет".encode())
print(encoded, base64.b64decode(encoded).decode())
Пароли хешируют медленными алгоритмами с солью: hashlib.scrypt, bcrypt, argon2 (Django делает это сам).
enum
from enum import Enum, IntEnum, StrEnum, auto, Flag
class Status(StrEnum):
NEW = auto()
PAID = auto()
SHIPPED = auto()
class Perm(Flag):
READ = auto()
WRITE = auto()
DELETE = auto()
print(Status.PAID, Status("new"), Status.PAID == "paid", list(Status))
rights = Perm.READ | Perm.WRITE
print(rights, Perm.WRITE in rights, Perm.DELETE in rights)
Время и замеры
import time, timeit
start = time.perf_counter() # для замеров длительности
sum(range(100_000))
print(f"{(time.perf_counter() - start) * 1000:.2f} мс")
print(time.time()) # Unix-время (секунды с 1970)
print(timeit.timeit("'-'.join(map(str, range(100)))", number=1000))
Работа с системой (вне браузера)
Эти модули работают с ОС, поэтому в браузерной песочнице их показываем без запуска:
import os, sys, shutil, subprocess, argparse
from pathlib import Path
os.environ.get("HOME"); os.getcwd(); os.cpu_count()
sys.argv; sys.exit(1); sys.version
shutil.copy("a.txt", "b.txt"); shutil.rmtree("build"); shutil.which("git")
result = subprocess.run(["git", "status", "--short"], capture_output=True, text=True, check=True)
print(result.stdout)
# никогда не передавайте пользовательский ввод с shell=True — инъекция команд
parser = argparse.ArgumentParser(description="Импорт пользователей")
parser.add_argument("file", type=Path)
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("-n", "--limit", type=int, default=100)
args = parser.parse_args()
Ещё полезное
| Модуль | Для чего |
|---|---|
pathlib, json, csv, datetime, re |
следующий модуль курса |
itertools, functools, operator |
урок «itertools и functools» |
dataclasses, typing, abc, enum |
ООП и типизация |
copy, pprint, reprlib |
копирование и красивый вывод |
logging, traceback, pdb, warnings |
логирование и отладка |
unittest, unittest.mock, doctest |
тестирование |
threading, multiprocessing, concurrent.futures, asyncio |
конкурентность |
sqlite3 |
встроенная SQL-база |
urllib, http.server, socket, email, smtplib |
сеть |
zipfile, tarfile, gzip, tempfile, shutil |
архивы и файлы |
contextlib, weakref, gc, sys, inspect, dis |
внутренности |
Совет для собеседования
Решая задачу, упоминайте подходящие инструменты: «здесь удобен Counter», «для очереди возьму deque, а не список — pop(0) у списка O(n)», «для top-k — heapq.nlargest». Это показывает опыт. Но будьте готовы написать и без них.
Итог
collections:Counter,defaultdict,deque,ChainMap,OrderedDict.heapq— куча,bisect— двоичный поиск в отсортированном списке.decimal— для денег,statistics— для статистики,math— математика.random— для симуляций,secrets— для безопасности;uuid,hashlib,hmac,base64.subprocess,argparse,shutil,os,sys— работа с системой и CLI.
Практика решено 0 из 5
Напишите top_words(text, n): вернуть список n самых частых слов текста (в нижнем регистре)
в виде пар (слово, количество). Используйте collections.Counter.
Слова — последовательности букв (знаки препинания отбросить): re.findall(r"\w+", ...).
Какие тесты проверяют код (3)
assert top_words("Мама мыла раму. Мама, мама!", 1) == [("мама", 3)]
assert top_words("a b b c c c", 2) == [("c", 3), ("b", 2)]
assert top_words("", 3) == []
import re
from collections import Counter
def top_words(text, n):
words = re.findall(r"\w+", text.lower())
return Counter(words).most_common(n)
Реализуйте RecentHistory(maxlen) — историю последних действий:
- add(item) добавляет действие; если их больше maxlen, самое старое вытесняется;
- last() возвращает список действий от новых к старым.
Используйте collections.deque(maxlen=...).
Какие тесты проверяют код (3)
h = RecentHistory(3); [h.add(x) for x in "abcde"]; assert h.last() == ["e", "d", "c"] h = RecentHistory(2); assert h.last() == [] from collections import deque; assert isinstance(RecentHistory(1).items, deque)
from collections import deque
class RecentHistory:
def __init__(self, maxlen):
self.items = deque(maxlen=maxlen)
def add(self, item):
self.items.append(item)
def last(self):
return list(reversed(self.items))
Посчитайте сумму денег без ошибок округления: money_sum(amounts) принимает список строк
вида "0.10" и возвращает строку суммы с двумя знаками ("0.30"). Используйте decimal.Decimal,
округление — «половина вверх» (ROUND_HALF_UP), как в бухгалтерии: "1.015" → "1.02".
Какие тесты проверяют код (4)
assert money_sum(["0.10", "0.20"]) == "0.30" assert money_sum(["1.005"] * 1000) == "1005.00" assert money_sum([]) == "0.00" assert money_sum(["1.015"]) == "1.02" and money_sum(["2.675"]) == "2.68"
from decimal import Decimal, ROUND_HALF_UP
def money_sum(amounts):
total = sum((Decimal(a) for a in amounts), Decimal("0"))
return str(total.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))
Какой модуль подходит для задачи?
| random | secrets | uuid | hashlib | |
|---|---|---|---|---|
| Перемешать вопросы теста | ||||
| Сгенерировать токен сброса пароля | ||||
| Уникальный идентификатор заказа | ||||
| Контрольная сумма файла SHA-256 |
Какую сложность имеют deque.appendleft() и list.insert(0, x)?
Войдите, чтобы отмечать пройденные уроки.