Python & AI Tutorials Logo
Программирование Python

39. Ключевые модули стандартной библиотеки

Стандартная библиотека(standard library) Python — это набор модулей, которые поставляются вместе с Python: вам не нужно устанавливать ничего дополнительно, чтобы ими пользоваться. Эти модули предоставляют мощные инструменты для типичных задач программирования: генерация случайных чисел, работа с датами и временем, обмен данными с другими программами и использование специализированных структур данных, которые выходят за рамки базовых списков и словарей.

В этой главе мы рассмотрим пять ключевых модулей стандартной библиотеки, которые вы будете часто использовать в реальной разработке на Python.

39.1) Генерация случайности с random

Модуль random предоставляет функции для генерации случайных чисел и выполнения случайных выборок. Это полезно для симуляций, игр, тестирования, выборки данных и любых ситуаций, где нужно непредсказуемое поведение.

39.1.1) Генерация случайных целых чисел с randint()

Функция randint() генерирует случайное целое число между двумя значениями, включительно по обоим концам:

python
import random
 
# Симуляция броска шестигранного кубика
die_roll = random.randint(1, 6)
print(f"You rolled: {die_roll}")  # Output: You rolled: 4 (varies each run)
 
# Генерация случайного возраста между 18 и 65
age = random.randint(18, 65)
print(f"Random age: {age}")  # Output: Random age: 42 (varies)

Обратите внимание, что и начальное, и конечное значения включаются в возможные результаты. randint(1, 6) может вернуть 1, 2, 3, 4, 5 или 6 — возможны все шесть значений.

Вот практический пример, который имитирует несколько бросков кубиков:

python
import random
 
# Симуляция броска двух кубиков и вычисление их суммы
die1 = random.randint(1, 6)
die2 = random.randint(1, 6)
total = die1 + die2
 
print(f"Die 1: {die1}")  # Output: Die 1: 3 (varies)
print(f"Die 2: {die2}")  # Output: Die 2: 5 (varies)
print(f"Total: {total}")  # Output: Total: 8 (varies)
 
if total == 7:
    print("Lucky seven!")
elif total == 2 or total == 12:
    print("Snake eyes or boxcars!")

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

39.1.2) Генерация случайных чисел с плавающей точкой

Для случайных десятичных чисел используйте random() (возвращает float между 0.0 и 1.0) или uniform() (возвращает float между двумя заданными значениями):

python
import random
 
# Сгенерировать случайное число с плавающей точкой между 0.0 и 1.0 (0.0 включено, 1.0 исключено)
probability = random.random()
print(f"Random probability: {probability:.4f}")  # Output: Random probability: 0.7284 (varies)
 
# Сгенерировать случайную температуру между 15.0 и 30.0 градусами
temperature = random.uniform(15.0, 30.0)
print(f"Temperature: {temperature:.2f}°C")  # Output: Temperature: 23.47°C (varies)
 
# Сгенерировать случайную цену между $10.00 и $99.99
price = random.uniform(10.0, 99.99)
print(f"Price: ${price:.2f}")  # Output: Price: $45.67 (varies)

Функция random() полезна, когда вам нужно значение вероятности или процент. Функция uniform() лучше, когда вам нужно случайное десятичное число в конкретном диапазоне.

39.1.3) Случайный выбор с choice()

Функция choice() случайным образом выбирает один элемент из последовательности (список(list), кортеж(tuple) или строка(string)):

python
import random
 
# Случайным образом выбрать цвет
colors = ["red", "blue", "green", "yellow", "purple"]
selected_color = random.choice(colors)
print(f"Selected color: {selected_color}")  # Output: Selected color: green (varies)
 
# Случайным образом выбрать победителя среди участников
participants = ["Alice", "Bob", "Charlie", "Diana"]
winner = random.choice(participants)
print(f"The winner is: {winner}")  # Output: The winner is: Bob (varies)
 
# Случайным образом выбрать символ из строки
vowels = "aeiou"
random_vowel = random.choice(vowels)
print(f"Random vowel: {random_vowel}")  # Output: Random vowel: i (varies)

Это особенно полезно для игр, случайной выборки или выбора случайных тестовых данных. Каждый элемент в последовательности имеет равную вероятность быть выбранным.

Вот более сложный пример, который имитирует простую викторину:

python
import random
 
# Вопросы викторины с их ответами
questions = [
    ("What is 2 + 2?", "4"),
    ("What is the capital of France?", "Paris"),
    ("What color is the sky?", "blue")
]
 
# Случайным образом выбрать вопрос
question, correct_answer = random.choice(questions)
print(f"Question: {question}")
 
user_answer = input("Your answer: ")
if user_answer.lower() == correct_answer.lower():
    print("Correct!")
else:
    print(f"Wrong! The answer was: {correct_answer}")

39.1.4) Выбор нескольких случайных элементов с sample()

Когда нужно выбрать несколько уникальных элементов из последовательности, используйте sample(). Это похоже на вытягивание карт из колоды без возвращения — как только элемент выбран, он больше не будет выбран снова:

python
import random
 
# Выбрать 3 случайных студентов для группового проекта
students = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
group = random.sample(students, 3)
print(f"Group members: {group}")  # Output: Group members: ['Diana', 'Alice', 'Frank'] (varies)
 
# Вытянуть 5 лотерейных чисел от 1 до 50 (без повторов)
lottery_numbers = random.sample(range(1, 51), 5)
lottery_numbers.sort()  # Отсортировать для отображения
print(f"Lottery numbers: {lottery_numbers}")  # Output: Lottery numbers: [7, 15, 23, 38, 49] (varies)

Второй аргумент sample() задаёт, сколько элементов выбрать. Число должно быть меньше или равно длине последовательности — нельзя выбрать больше элементов, чем доступно.

39.1.5) Перемешивание последовательностей с shuffle()

Функция shuffle() случайным образом переупорядочивает элементы списка(list) на месте (изменяя исходный список):

python
import random
 
# Перемешать колоду карт
cards = ["A♠", "K♠", "Q♠", "J♠", "10♠", "9♠", "8♠", "7♠"]
print(f"Original: {cards}")
random.shuffle(cards)
print(f"Shuffled: {cards}")  # Output: Shuffled: ['Q♠', '7♠', 'A♠', '10♠', '9♠', 'J♠', 'K♠', '8♠'] (varies)
 
# Перемешать вопросы викторины для случайного порядка
questions = ["Question 1", "Question 2", "Question 3", "Question 4"]
random.shuffle(questions)
print(f"Randomized order: {questions}")  # Output: Randomized order: ['Question 3', 'Question 1', 'Question 4', 'Question 2'] (varies)

Функции модуля random

randint: Случайные целые числа

random/uniform: Случайные числа с плавающей точкой

choice: Выбрать один элемент

sample: Выбрать несколько уникальных элементов

shuffle: Переупорядочить список на месте

Включительно по обоим концам

random: от 0.0 до 1.0

uniform: Произвольный диапазон

Равная вероятность для каждого

Без повторов

Изменяет исходный список

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

Модуль datetime предоставляет классы для работы с датами, временем и интервалами времени. Это важно для планирования, логирования, расчёта длительностей и любых приложений, которым нужно отслеживать, когда происходят события.

39.2.1) Получение текущих даты и времени

Класс datetime представляет конкретный момент времени, включающий и дату, и время:

python
from datetime import datetime
 
# Получить текущие дату и время
now = datetime.now()
print(f"Current datetime: {now}")
# Output: Current datetime: 2026-01-02 14:30:45.123456
 
# Доступ к отдельным компонентам
print(f"Year: {now.year}")      # Output: Year: 2026
print(f"Month: {now.month}")    # Output: Month: 1
print(f"Day: {now.day}")        # Output: Day: 2
print(f"Hour: {now.hour}")      # Output: Hour: 14
print(f"Minute: {now.minute}")  # Output: Minute: 30
print(f"Second: {now.second}")  # Output: Second: 45

Чтобы получить только дату (без времени), используйте класс date:

python
from datetime import date
 
# Получить сегодняшнюю дату
today = date.today()
print(f"Today: {today}")  # Output: Today: 2026-01-02
 
print(f"Year: {today.year}")    # Output: Year: 2026
print(f"Month: {today.month}")  # Output: Month: 1
print(f"Day: {today.day}")      # Output: Day: 2

39.2.2) Создание конкретных дат и времени

Вы можете создавать объекты datetime и date для конкретных моментов времени:

python
from datetime import datetime, date
 
# Создать конкретную дату
birthday = date(1995, 7, 15)
print(f"Birthday: {birthday}")  # Output: Birthday: 1995-07-15
 
# Создать конкретный datetime
meeting = datetime(2026, 3, 15, 14, 30)  # March 15, 2026 at 2:30 PM
print(f"Meeting: {meeting}")  # Output: Meeting: 2026-03-15 14:30:00

Это полезно для представления дедлайнов, встреч, исторических дат или любой фиксированной точки во времени:

python
from datetime import date
 
# Важные даты в проекте
project_start = date(2026, 1, 15)
project_end = date(2026, 6, 30)
 
print(f"Project duration: {project_start} to {project_end}")
# Output: Project duration: 2026-01-15 to 2026-06-30

39.2.3) Вычисление разницы во времени с timedelta

Класс timedelta представляет длительность — разницу между двумя датами или моментами времени. Его можно использовать, чтобы вычислить, сколько времени прошло, или чтобы добавлять/вычитать время к датам:

python
from datetime import date, timedelta
 
# Рассчитать возраст
birth_date = date(1995, 7, 15)
today = date(2026, 1, 2)
age_delta = today - birth_date
 
print(f"Days since birth: {age_delta.days}")  # Output: Days since birth: 11128
print(f"Years (approximate): {age_delta.days // 365}")  # Output: Years (approximate): 30

Когда вы вычитаете одну дату из другой, вы получаете объект timedelta. Атрибут days сообщает количество дней в этой длительности.

Также можно создавать объекты timedelta напрямую, чтобы представлять конкретные длительности:

python
from datetime import date, timedelta
 
# Добавить дни к дате
today = date(2026, 1, 2)
one_week = timedelta(days=7)
next_week = today + one_week
 
print(f"Today: {today}")        # Output: Today: 2026-01-02
print(f"Next week: {next_week}")  # Output: Next week: 2026-01-09
 
# Вычесть дни из даты
thirty_days_ago = today - timedelta(days=30)
print(f"30 days ago: {thirty_days_ago}")  # Output: 30 days ago: 2025-12-03

timedelta может представлять дни, секунды, микросекунды, миллисекунды, минуты, часы и недели:

python
from datetime import datetime, timedelta
 
# Рассчитать дедлайн
now = datetime(2026, 1, 2, 14, 30)
deadline = now + timedelta(hours=48, minutes=30)
 
print(f"Current time: {now}")    # Output: Current time: 2026-01-02 14:30:00
print(f"Deadline: {deadline}")   # Output: Deadline: 2026-01-04 15:00:00
 
# Рассчитать оставшееся время
time_left = deadline - now
print(f"Hours remaining: {time_left.total_seconds() / 3600}")  # Output: Hours remaining: 48.5

Метод total_seconds() преобразует всю длительность в секунды, которые затем можно перевести в часы, минуты или любую другую единицу.

Вот практический пример вычисления вех проекта:

python
from datetime import date, timedelta
 
# Планирование проекта
project_start = date(2026, 1, 15)
sprint_duration = timedelta(weeks=2)
 
sprint_1_end = project_start + sprint_duration
sprint_2_end = sprint_1_end + sprint_duration
sprint_3_end = sprint_2_end + sprint_duration
 
print(f"Sprint 1: {project_start} to {sprint_1_end}")
# Output: Sprint 1: 2026-01-15 to 2026-01-29
print(f"Sprint 2: {sprint_1_end} to {sprint_2_end}")
# Output: Sprint 2: 2026-01-29 to 2026-02-12
print(f"Sprint 3: {sprint_2_end} to {sprint_3_end}")
# Output: Sprint 3: 2026-02-12 to 2026-02-26

39.2.4) Сравнение дат и времени

Объекты date и datetime можно сравнивать с помощью стандартных операторов сравнения:

python
from datetime import date
 
# Сравнить даты
date1 = date(2026, 1, 15)
date2 = date(2026, 2, 20)
date3 = date(2026, 1, 15)
 
print(date1 < date2)   # Output: True
print(date1 == date3)  # Output: True
print(date2 > date1)   # Output: True

Это полезно для проверки дедлайнов, валидации диапазонов дат и сортировки дат:

python
from datetime import date
 
# Проверить, находится ли дата в прошлом
event_date = date(2025, 12, 25)
today = date(2026, 1, 2)
 
if event_date < today:
    print("This event has already passed")  # Output: This event has already passed
else:
    print("This event is upcoming")
 
# Отсортировать список дат
important_dates = [
    date(2026, 3, 15),
    date(2026, 1, 10),
    date(2026, 2, 28)
]
 
important_dates.sort()
print("Dates in order:")  # Output: Dates in order:
for d in important_dates:
    print(f"  {d}")
# Output:
#   2026-01-10
#   2026-02-28
#   2026-03-15

39.2.5) Форматирование дат и времени с strftime()

Метод strftime() (string format time) преобразует даты и время в форматированные строки. Формат задаётся специальными кодами:

python
from datetime import datetime
 
now = datetime(2026, 1, 2, 14, 30, 45)
 
# Распространённые форматы дат
print(now.strftime("%Y-%m-%d"))           # Output: 2026-01-02
print(now.strftime("%m/%d/%Y"))           # Output: 01/02/2026
print(now.strftime("%B %d, %Y"))          # Output: January 02, 2026
print(now.strftime("%A, %B %d, %Y"))      # Output: Friday, January 02, 2026
 
# Распространённые форматы времени
print(now.strftime("%H:%M:%S"))           # Output: 14:30:45
print(now.strftime("%I:%M %p"))           # Output: 02:30 PM
 
# Комбинированные форматы
print(now.strftime("%Y-%m-%d %H:%M:%S"))  # Output: 2026-01-02 14:30:45
print(now.strftime("%B %d, %Y at %I:%M %p"))  # Output: January 02, 2026 at 02:30 PM

Распространённые коды формата:

CodeDescriptionExample
%YГод с веком2026
%mМесяц как число с ведущим нулём (01-12)01
%dДень как число с ведущим нулём (01-31)02
%BПолное название месяцаJanuary
%bКороткое название месяцаJan
%AПолное название дня неделиFriday
%aКороткое название дня неделиFri
%HЧасы в 24-часовом формате (00-23)14
%IЧасы в 12-часовом формате (01-12)02
%MМинуты (00-59)30
%SСекунды (00-59)45
%pAM/PMPM

Вот практический пример создания записи лога:

python
from datetime import datetime
 
def log_event(message):
    """Записать событие с временной меткой"""
    now = datetime.now()
    timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{timestamp}] {message}")
 
log_event("User logged in")
# Output: [2026-01-02 14:30:45] User logged in
 
log_event("File uploaded successfully")
# Output: [2026-01-02 14:30:45] File uploaded successfully

39.2.6) Разбор дат из строк с strptime()

Функция strptime() (string parse time) преобразует форматированные строки обратно в объекты datetime. Вы указываете те же коды формата, чтобы сообщить Python, как интерпретировать строку:

python
from datetime import datetime
 
# Разобрать разные форматы дат
date_str1 = "2026-01-15"
date1 = datetime.strptime(date_str1, "%Y-%m-%d")
print(f"Parsed: {date1}")  # Output: Parsed: 2026-01-15 00:00:00
 
date_str2 = "January 15, 2026"
date2 = datetime.strptime(date_str2, "%B %d, %Y")
print(f"Parsed: {date2}")  # Output: Parsed: 2026-01-15 00:00:00
 
# Разобрать datetime со временем
datetime_str = "2026-01-15 14:30:00"
dt = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
print(f"Parsed: {dt}")  # Output: Parsed: 2026-01-15 14:30:00

Это необходимо при чтении дат из файлов, пользовательского ввода или внешних источников данных:

python
from datetime import datetime
 
# Разобрать пользовательский ввод
user_input = "03/15/2026"
try:
    event_date = datetime.strptime(user_input, "%m/%d/%Y")
    print(f"Event scheduled for: {event_date.strftime('%B %d, %Y')}")
    # Output: Event scheduled for: March 15, 2026
except ValueError:
    print("Invalid date format. Please use MM/DD/YYYY")

Важно: строка формата должна точно соответствовать входной строке, иначе вы получите ValueError:

python
from datetime import datetime
 
# Это завершится ошибкой — формат не совпадает
try:
    datetime.strptime("2026-01-15", "%m/%d/%Y")  # Wrong format
except ValueError as e:
    print(f"Error: {e}")
    # Output: Error: time data '2026-01-15' does not match format '%m/%d/%Y'

Модуль datetime

datetime.now: Текущие дата/время

date.today: Текущая дата

datetime/date: Создание конкретных дат

timedelta: Длительности времени

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

strptime: Разбор из строки

Добавление/вычитание к датам

Вычисление разницы

%Y, %m, %d, %H, %M, %S

Должно точно совпадать с форматом

39.3) Чтение и запись данных JSON

JSON (JavaScript Object Notation) — это текстовый формат для хранения и обмена структурированными данными. Это самый распространённый формат для веб-API, конфигурационных файлов и обмена данными между программами. Модуль json в Python упрощает преобразование между структурами данных Python и текстом JSON.

39.3.1) Понимание структуры JSON

JSON похож на словари(dict) и списки(list) Python, но есть некоторые отличия:

JSON поддерживает следующие типы данных:

  • Objects (как словари Python): {"name": "Alice", "age": 30}
  • Arrays (как списки Python): [1, 2, 3, 4]
  • Strings: "hello" (должны использоваться двойные кавычки)
  • Numbers: 42, 3.14
  • Booleans: true, false (в нижнем регистре)
  • Null: null (как None в Python)

Ключевые отличия от Python:

  • JSON использует true/false/null вместо True/False/None в Python
  • Строки в JSON должны использовать двойные кавычки ("text"), а не одинарные
  • JSON не поддерживает кортежи(tuple), множества(set) или пользовательские объекты напрямую

Вот как выглядит JSON-данные:

json
{
    "name": "Alice Johnson",
    "age": 30,
    "email": "alice@example.com",
    "is_active": true,
    "scores": [85, 92, 78, 95],
    "address": {
        "street": "123 Main St",
        "city": "Springfield",
        "zip": "12345"
    }
}

Примечание: Это чистый текст JSON, а не код Python. Обратите внимание на true в нижнем регистре и использование двойных кавычек.

39.3.2) Преобразование данных Python в JSON с dumps()

Функция dumps() (dump string) преобразует структуры данных Python в строки в формате JSON:

python
import json
 
student = {
    "name": "Alice Johnson",
    "age": 30,
    "email": "alice@example.com",
    "is_active": True,
    "scores": [85, 92, 78, 95]
}
 
# Преобразовать словарь в JSON
json_string = json.dumps(student)
print(json_string)
# Output: {"name": "Alice Johnson", "age": 30, "email": "alice@example.com", "is_active": true, "scores": [85, 92, 78, 95]}
 
print(type(json_string))  # Output: <class 'str'>

Обратите внимание, как True в Python превратился в true в JSON в выводе. Функция dumps() автоматически обрабатывает такие преобразования.

Для более читаемого вывода используйте параметр indent:

python
import json
 
student = {
    "name": "Alice Johnson",
    "age": 30,
    "scores": [85, 92, 78, 95]
}
 
# Красивый вывод с отступами
json_string = json.dumps(student, indent=2)
print(json_string)
# Output:
# {
#   "name": "Alice Johnson",
#   "age": 30,
#   "scores": [
#     85,
#     92,
#     78,
#     95
#   ]
# }

Параметр indent задаёт, сколько пробелов использовать для каждого уровня отступа. Это делает JSON гораздо более читаемым, особенно для сложных вложенных структур.

39.3.3) Преобразование JSON в данные Python с loads()

Функция loads() (load string) преобразует строки в формате JSON обратно в структуры данных Python:

python
import json
 
# JSON-строка (как если бы вы получили её от web API)
json_string = '{"name": "Bob Smith", "age": 25, "scores": [90, 88, 92]}'
 
# Преобразовать в словарь Python
student = json.loads(json_string)
print(student)  # Output: {'name': 'Bob Smith', 'age': 25, 'scores': [90, 88, 92]}
print(type(student))  # Output: <class 'dict'>
 
# Доступ к данным как к любому словарю Python
print(f"Name: {student['name']}")  # Output: Name: Bob Smith
print(f"Average score: {sum(student['scores']) / len(student['scores'])}")
# Output: Average score: 90.0

true, false и null из JSON автоматически преобразуются в True, False и None в Python:

python
import json
 
json_string = '{"active": true, "verified": false, "middle_name": null}'
data = json.loads(json_string)
 
print(data)  # Output: {'active': True, 'verified': False, 'middle_name': None}
print(type(data["active"]))  # Output: <class 'bool'>
print(type(data["middle_name"]))  # Output: <class 'NoneType'>

39.3.4) Запись JSON в файлы с dump()

Функция dump() записывает данные Python напрямую в файл в формате JSON:

python
import json
 
# Записи о студентах
students = [
    {"name": "Alice", "age": 20, "gpa": 3.8},
    {"name": "Bob", "age": 22, "gpa": 3.5},
    {"name": "Charlie", "age": 21, "gpa": 3.9}
]
 
# Записать в JSON-файл
with open("students.json", "w") as file:
    json.dump(students, file, indent=2)
 
print("Data written to students.json")
# Output: Data written to students.json

После выполнения этого кода файл students.json содержит:

json
[
  {
    "name": "Alice",
    "age": 20,
    "gpa": 3.8
  },
  {
    "name": "Bob",
    "age": 22,
    "gpa": 3.5
  },
  {
    "name": "Charlie",
    "age": 21,
    "gpa": 3.9
  }
]

Почему использовать dump(), а не dumps()? Функция dump() пишет напрямую в файл, что эффективнее, чем сначала преобразовать данные в строку, а затем записать строку. Используйте dump() для файлов и dumps(), когда вам нужен JSON в виде строки (например, чтобы отправить по сети).

39.3.5) Чтение JSON из файлов с load()

Функция load() читает данные JSON из файла и преобразует их в структуры данных Python:

python
import json
 
# Прочитать JSON-файл, который мы создали ранее
with open("students.json", "r") as file:
    students = json.load(file)
 
print(f"Loaded {len(students)} students")  # Output: Loaded 3 students
 
# Работать с данными
for student in students:
    print(f"{student['name']}: GPA {student['gpa']}")
# Output:
# Alice: GPA 3.8
# Bob: GPA 3.5
# Charlie: GPA 3.9

39.3.6) Обработка ошибок JSON

При работе с JSON вы можете столкнуться с некорректными данными. Всегда обрабатывайте возможные ошибки:

python
import json
 
# Некорректный JSON — отсутствует закрывающая кавычка
invalid_json = '{"name": "Alice", "age": 30'
 
try:
    data = json.loads(invalid_json)
except json.JSONDecodeError as e:
    print(f"Invalid JSON: {e}")
    # Output: Invalid JSON: Expecting ',' delimiter: line 1 column 28 (char 27)

Это особенно важно при чтении JSON из внешних источников (файлы, web API, пользовательский ввод), где вы не можете гарантировать валидность данных:

python
import json
 
def load_config(filename):
    """Загрузить конфигурацию из JSON-файла с обработкой ошибок"""
    try:
        with open(filename, "r") as file:
            config = json.load(file)
            return config
    except FileNotFoundError:
        print(f"Config file '{filename}' not found")
        return None
    except json.JSONDecodeError as e:
        print(f"Invalid JSON in '{filename}': {e}")
        return None
 
# Попробовать загрузить конфигурацию
config = load_config("config.json")
if config:
    print(f"Configuration loaded: {config}")
else:
    print("Using default configuration")

39.3.7) Практический пример JSON: сохранение и загрузка состояния приложения

Вот полный пример, показывающий, как сохранять и загружать данные приложения:

python
import json
 
def save_game_state(filename, player_data):
    """Сохранить состояние игры в JSON-файл"""
    with open(filename, "w") as file:
        json.dump(player_data, file, indent=2)
    print(f"Game saved to {filename}")
 
def load_game_state(filename):
    """Загрузить состояние игры из JSON-файла"""
    try:
        with open(filename, "r") as file:
            player_data = json.load(file)
        print(f"Game loaded from {filename}")
        return player_data
    except FileNotFoundError:
        print("No saved game found")
        return None
 
# Данные игры
player = {
    "name": "Hero",
    "level": 5,
    "health": 85,
    "inventory": ["sword", "shield", "potion"],
    "position": {"x": 10, "y": 20}
}
 
# Сохранить игру
save_game_state("savegame.json", player)
# Output: Game saved to savegame.json
 
# Позже загрузить игру
loaded_player = load_game_state("savegame.json")
# Output: Game loaded from savegame.json
 
if loaded_player:
    print(f"Welcome back, {loaded_player['name']}!")
    print(f"Level: {loaded_player['level']}, Health: {loaded_player['health']}")
    # Output:
    # Welcome back, Hero!
    # Level: 5, Health: 85

Модуль json

dumps: Python → JSON-строка

loads: JSON-строка → Python

dump: Python → JSON-файл

load: JSON-файл → Python

Параметр indent для читаемости

Обрабатывает преобразования типов

Эффективнее, чем dumps + write

Обрабатывать JSONDecodeError

39.4) Практичные контейнеры в collections

Модуль collections предоставляет специализированные типы контейнеров, которые расширяют встроенные контейнеры Python (списки(list), словари(dict), множества(set)) дополнительной функциональностью. Эти контейнеры более элегантно решают распространённые задачи, чем использование базовых структур данных.

39.4.1) Подсчёт элементов с Counter

Класс Counter предназначен для подсчёта хешируемых объектов. Это подкласс словаря, который хранит элементы как ключи и их количества как значения.

Что Counter принимает на вход:

  • Любой итерируемый объект(iterable) (список, строка, кортеж и т. д.)
  • Другой словарь с количествами
  • Именованные аргументы с количествами

Что Counter хранит:

  • Словарь, где ключи — это элементы, а значения — их количества
  • Пример: Counter(['a', 'b', 'a']) хранит {'a': 2, 'b': 1}

Ключевое преимущество перед обычными словарями:

  • Возвращает 0 для отсутствующих ключей вместо выбрасывания KeyError
  • Предоставляет методы, ориентированные на подсчёт, например most_common()
  • Поддерживает арифметические операции между счётчиками

Базовое использование

python
from collections import Counter
 
# Подсчитать буквы в слове
word = "mississippi"
letter_counts = Counter(word)
print(letter_counts)
# Output: Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
 
# Доступ к количествам как у словаря
print(f"Number of 'i's: {letter_counts['i']}")
# Output: Number of 'i's: 4
 
print(f"Number of 'z's: {letter_counts['z']}")
# Output: Number of 'z's: 0 (returns 0 for missing keys, no KeyError!)

Создание Counter из разных источников

python
from collections import Counter
 
# Из списка
votes = ["Alice", "Bob", "Alice", "Charlie", "Alice", "Bob", "Alice"]
vote_counts = Counter(votes)
print(vote_counts)
# Output: Counter({'Alice': 4, 'Bob': 2, 'Charlie': 1})
 
# Из строки (считает каждый символ)
letter_counts = Counter("hello")
print(letter_counts)
# Output: Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
 
# Из словаря
existing_counts = {'apple': 3, 'banana': 2}
fruit_counts = Counter(existing_counts)
print(fruit_counts)
# Output: Counter({'apple': 3, 'banana': 2})
 
# Из именованных аргументов
color_counts = Counter(red=5, blue=3, green=2)
print(color_counts)
# Output: Counter({'red': 5, 'blue': 3, 'green': 2})

Поиск самых частых элементов с most_common()

Сигнатура метода: most_common(n=None)

Параметры:

  • n (необязательно): количество самых частых элементов, которые нужно вернуть
  • Если n опущен или равен None, возвращает все элементы

Возвращает:

  • Список кортежей(tuple) (item, count)
  • Отсортированный по количеству, по убыванию
  • Если количества равны, элементы идут в порядке первого появления
python
from collections import Counter
 
# Анализ частоты слов в тексте
text = "the quick brown fox jumps over the lazy dog the fox"
words = text.split()
word_counts = Counter(words)
 
# Получить 3 самых частых слова
top_3 = word_counts.most_common(3)
print(top_3)
# Output: [('the', 3), ('fox', 2), ('quick', 1)]

Арифметические операции над Counter

Вы можете складывать, вычитать и выполнять другие операции с объектами Counter:

python
from collections import Counter
 
# Подсчитать элементы в двух группах
group1 = Counter(["apple", "banana", "apple", "orange"])
print(group1)
# Output: Counter({'apple': 2, 'banana': 1, 'orange': 1})
 
group2 = Counter(["banana", "banana", "grape", "apple"])
print(group2)
# Output: Counter({'banana': 2, 'grape': 1, 'apple': 1})
 
# Сложить количества
combined = group1 + group2
print(combined)
# Output: Counter({'apple': 3, 'banana': 3, 'orange': 1, 'grape': 1})
 
# Вычесть количества (оставляет только положительные результаты)
difference = group1 - group2
print(difference)
# Output: Counter({'apple': 1, 'orange': 1})
# banana: 1 - 2 = -1 (negative, so excluded)
# grape: not in group1, so excluded

Практический пример: анализ оценок студентов

python
from collections import Counter
 
# Распределение оценок
grades = ["A", "B", "A", "C", "B", "A", "B", "D", "A", "B", "C", "A"]
grade_counts = Counter(grades)
 
print(f"Total students: {len(grades)}")
# Output: Total students: 12
 
print("\nGrade Distribution:")
for grade, count in grade_counts.most_common():
    percentage = (count / len(grades)) * 100
    bar = "█" * count
    print(f"  {grade}: {count} students ({percentage:4.1f}%) {bar}")
# Output:
# Grade Distribution:
#   A: 5 students (41.7%) █████
#   B: 4 students (33.3%) ████
#   C: 2 students (16.7%) ██
#   D: 1 students ( 8.3%) █

39.4.2) Словари со значениями по умолчанию с defaultdict

Класс defaultdict — это подкласс словаря(dict), который автоматически создаёт записи со значением по умолчанию, когда вы обращаетесь к отсутствующему ключу. Это устраняет необходимость проверять существование ключей перед использованием.

Что defaultdict принимает на вход:

  • Функцию default factory (обязательно): вызываемый объект, который возвращает значение по умолчанию для отсутствующих ключей
  • Любые аргументы, которые принимает обычный dict (пары ключ-значение, другой словарь, именованные аргументы)

Ключевое преимущество перед обычными словарями:

  • Не нужно проверять, существует ли ключ перед использованием
  • Автоматически инициализирует отсутствующие ключи значением по умолчанию
  • Более чистый и читаемый код для операций группировки, подсчёта и накопления

Понимание default factory

Когда вы создаёте defaultdict, вы должны указать default factory — вызываемый объект (функцию), который не принимает аргументов и возвращает значение по умолчанию. Распространённые default factory:

  • int — возвращает 0 (полезно для подсчёта)
  • list — возвращает [] (полезно для группировки элементов)
  • set — возвращает set() (полезно для сбора уникальных элементов)
  • str — возвращает '' (полезно для конкатенации строк)
  • lambda: value — возвращает произвольное значение по умолчанию
python
from collections import defaultdict
 
# Разные default factory
counts = defaultdict(int)        # Для отсутствующих ключей возвращается 0
groups = defaultdict(list)       # Для отсутствующих ключей возвращается []
unique = defaultdict(set)        # Для отсутствующих ключей возвращается set()
custom = defaultdict(lambda: "N/A")  # Для отсутствующих ключей возвращается "N/A"
 
# Проверка на отсутствующих ключах
print(counts['missing'])     # Output: 0
print(groups['missing'])     # Output: []
print(unique['missing'])     # Output: set()
print(custom['missing'])     # Output: N/A

Базовое использование: подсчёт с defaultdict

Сравнение обычного словаря и defaultdict для подсчёта:

python
from collections import defaultdict
 
word = "mississippi"
 
# Обычный словарь — нужно проверять, существует ли ключ
regular_dict = {}
for letter in word:
    if letter not in regular_dict:
        regular_dict[letter] = 0
    regular_dict[letter] += 1
 
print(regular_dict)
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}
 
# defaultdict — автоматически создаёт записи со значением по умолчанию
letter_counts = defaultdict(int)  # int() возвращает 0
for letter in word:
    letter_counts[letter] += 1  # Не нужно проверять, существует ли ключ!
 
print(dict(letter_counts))
# Output: {'m': 1, 'i': 4, 's': 4, 'p': 2}

Как это работает:

  1. Когда вы обращаетесь к letter_counts[letter] для новой буквы, defaultdict вызывает int(), который возвращает 0
  2. Ключ создаётся со значением 0, затем += 1 делает его равным 1
  3. Для существующих ключей он ведёт себя как обычный словарь

Группировка элементов с defaultdict(list)

Распространённый сценарий — группировать элементы по категориям:

python
from collections import defaultdict
 
students = [
    ("Alice", "A"),
    ("Bob", "B"),
    ("Charlie", "A"),
    ("Diana", "C"),
    ("Eve", "B"),
    ("Frank", "A")
]
 
# Сгруппировать студентов по оценке
# С defaultdict — чисто и просто
students_by_grade = defaultdict(list)
for name, grade in students:
    students_by_grade[grade].append(name)
 
print(dict(students_by_grade))
# Output: {'A': ['Alice', 'Charlie', 'Frank'], 'B': ['Bob', 'Eve'], 'C': ['Diana']}
 
# Обратиться к оценке, которой ещё нет
print(students_by_grade["D"])  # Output: [] (empty list, not KeyError!)

Как это работает:

  1. Когда вы обращаетесь к students_by_grade[grade] для новой оценки, defaultdict вызывает list(), который возвращает []
  2. Ключ создаётся с пустым списком, затем .append(name) добавляет первого студента
  3. Для существующих оценок он просто добавляет в уже существующий список

Создание defaultdict из существующего словаря

Можно инициализировать defaultdict существующими данными:

python
from collections import defaultdict
 
# Начать с уже существующих подсчётов
existing_data = {'apple': 5, 'banana': 3}
 
# Создать defaultdict из существующего словаря
fruit_counts = defaultdict(int, existing_data)
 
# Добавить ещё подсчётов
fruit_counts['apple'] += 2     # 5 + 2 = 7
fruit_counts['orange'] += 1    # 0 + 1 = 1 (new key, starts at 0)
 
print(dict(fruit_counts))
# Output: {'apple': 7, 'banana': 3, 'orange': 1}

Пользовательская default factory

В качестве default factory можно передать любой вызываемый объект:

python
from collections import defaultdict
 
# Использовать lambda для пользовательских значений по умолчанию
page_views = defaultdict(lambda: {'views': 0, 'unique': 0})
 
page_views['home']['views'] = 100
page_views['home']['unique'] = 75
 
print(page_views['home'])
# Output: {'views': 100, 'unique': 75}
 
print(page_views['about'])  # New key gets default dictionary
# Output: {'views': 0, 'unique': 0}

Важные замечания

Доступ vs. проверка наличия ключей:

python
from collections import defaultdict
 
counts = defaultdict(int)
 
# Доступ к отсутствующему ключу СОЗДАЁТ его
value = counts['missing']  # Creates 'missing' with value 0
print('missing' in counts)  # Output: True
 
# Чтобы проверить без создания, используйте 'in' или .get()
counts2 = defaultdict(int)
print('missing' in counts2)      # Output: False (doesn't create key)
print(counts2.get('missing'))    # Output: None (doesn't create key)

39.5) (Необязательно) Полезные инструменты итерации

Модуль itertools предоставляет функции для создания эффективных итераторов(iterator). Эти инструменты помогают мощно работать с последовательностями, не создавая большие промежуточные списки.

39.5.1) Объединение итерируемых объектов с chain()

Функция chain() объединяет несколько итерируемых объектов в один итератор, который выдаёт элементы из каждого итерируемого объекта последовательно.

Что принимает chain():

  • Несколько итерируемых объектов (списки, кортежи, строки и т. д.) как отдельные аргументы

Что возвращает chain():

  • Итератор, который выдаёт все элементы из первого итерируемого объекта, затем все элементы из второго и т. д.

Ключевое преимущество:

  • Более экономно по памяти, чем конкатенация с + (не создаёт промежуточные списки)
  • Работает с любым итерируемым объектом, не только со списками
python
from itertools import chain
 
# Объединить несколько списков
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list3 = [7, 8, 9]
 
combined = chain(list1, list2, list3)
print(list(combined))  # Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Это более экономно по памяти, чем объединение списков с +, особенно для больших последовательностей:

python
from itertools import chain
 
# Обработать несколько источников данных, не создавая большой объединённый список
students_class_a = ["Alice", "Bob", "Charlie"]
students_class_b = ["Diana", "Eve", "Frank"]
students_class_c = ["Grace", "Henry", "Iris"]
 
# Пройти по всем студентам, не создавая объединённый список
for student in chain(students_class_a, students_class_b, students_class_c):
    print(f"Processing: {student}")
# Output:
# Processing: Alice
# Processing: Bob
# Processing: Charlie
# Processing: Diana
# Processing: Eve
# Processing: Frank
# Processing: Grace
# Processing: Henry
# Processing: Iris

Можно объединять разные типы итерируемых объектов:

python
from itertools import chain
 
# Объединить списки, кортежи и строки
numbers = [1, 2, 3]
letters = ("a", "b", "c")
word = "xyz"
 
combined = chain(numbers, letters, word)
print(list(combined))  # Output: [1, 2, 3, 'a', 'b', 'c', 'x', 'y', 'z']

39.5.2) Повторение элементов с cycle()

Функция cycle() создаёт бесконечный итератор, который повторно циклически проходит по элементам итерируемого объекта.

Что принимает cycle():

  • Один итерируемый объект (список, кортеж, строка и т. д.)

Что возвращает cycle():

  • Бесконечный итератор, который выдаёт элементы итерируемого объекта снова и снова
  • Достигнув конца, он начинает сначала

Ключевые характеристики:

  • Создаёт бесконечный итератор — сам по себе никогда не остановится
  • Должен использоваться с условием остановки (счётчик, break или zip())
  • Экономен по памяти: не создаёт копии данных
python
from itertools import cycle
 
# Создать бесконечный цикл цветов
colors = cycle(["red", "green", "blue"])
 
# Взять первые 10 цветов
for i, color in enumerate(colors):
    if i >= 10:
        break
    print(f"Item {i}: {color}")
# Output:
# Item 0: red
# Item 1: green
# Item 2: blue
# Item 3: red
# Item 4: green
# Item 5: blue
# Item 6: red
# Item 7: green
# Item 8: blue
# Item 9: red

Предупреждение: cycle() создаёт бесконечный итератор. Всегда используйте его с условием остановки (например, счётчиком или оператором break), иначе вы получите бесконечный цикл(loop).

Практический пример — чередование значений:

python
from itertools import cycle
 
# Чередовать два фоновых цвета для строк таблицы
row_colors = cycle(["white", "lightgray"])
 
rows = ["Row 1", "Row 2", "Row 3", "Row 4", "Row 5"]
for row, color in zip(rows, row_colors):
    print(f"{row}: background-color: {color}")
# Output:
# Row 1: background-color: white
# Row 2: background-color: lightgray
# Row 3: background-color: white
# Row 4: background-color: lightgray
# Row 5: background-color: white

Здесь мы используем zip() (который мы изучили в главе 37), чтобы сопоставить каждой строке цвет. Итератор cycle() автоматически повторяет цвета по мере необходимости.

39.5.3) Комбинирование chain() и cycle()

Вы можете комбинировать функции itertools для более сложных шаблонов:

python
from itertools import chain, cycle
 
# Создать шаблон, который циклично проходит по нескольким последовательностям
pattern1 = [1, 2, 3]
pattern2 = [10, 20]
 
# Объединить шаблоны, затем зациклить результат
combined_pattern = cycle(chain(pattern1, pattern2))
 
# Взять первые 12 значений
for i, value in enumerate(combined_pattern):
    if i >= 12:
        break
    print(value, end=" ")
# Output: 1 2 3 10 20 1 2 3 10 20 1 2
 
print()  # Newline

Это создаёт повторяющийся шаблон: 1, 2, 3, 10, 20, 1, 2, 3, 10, 20, ...

Вот практический пример создания ротационного расписания:

python
from itertools import cycle
 
# Создать ротационное расписание для членов команды
team_members = ["Alice", "Bob", "Charlie"]
schedule = cycle(team_members)
 
# Назначать задачи членам команды по кругу
tasks = [
    "Review code",
    "Write tests",
    "Update documentation",
    "Fix bug #123",
    "Implement feature X",
    "Deploy to staging"
]
 
print("Task Assignments:")
for task, assignee in zip(tasks, schedule):
    print(f"  {assignee}: {task}")
# Output:
# Task Assignments:
#   Alice: Review code
#   Bob: Write tests
#   Charlie: Update documentation
#   Alice: Fix bug #123
#   Bob: Implement feature X
#   Charlie: Deploy to staging

Модуль itertools

chain: Объединить итерируемые объекты

cycle: Повторять бесконечно

Экономнее по памяти, чем +

Работает с разными типами

Создаёт бесконечный итератор

Всегда использовать с условием остановки

Полезно для чередования шаблонов


В этой главе мы рассмотрели пять ключевых модулей стандартной библиотеки, которые расширяют возможности Python:

  • random: генерирует случайные числа, выполняет случайный выбор и перемешивает последовательности — важно для симуляций, игр и тестирования
  • datetime: работает с датами, временем и длительностями — рассчитывает возраст, планирует события и форматирует временные метки
  • json: обеспечивает обмен данными с другими программами с помощью универсального формата JSON — сохраняет состояние приложения, работает с web API и хранит конфигурацию
  • collections: предоставляет специализированные контейнеры, такие как Counter для подсчёта и defaultdict для автосоздания ключей
  • itertools: создаёт эффективные итераторы с chain() для объединения последовательностей и cycle() для повторяющихся шаблонов

Эти модули — часть стандартной библиотеки Python: они всегда доступны, хорошо протестированы и элегантно решают распространённые задачи программирования. По мере того как вы будете создавать более сложные программы, вы будете часто обращаться к этим инструментам. Они отражают философию Python «batteries included» — предоставление мощных, готовых к использованию решений для повседневных задач программирования.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai