Python для начинающих
1.24K subscribers
546 photos
3 videos
232 files
74 links
Python для начинающих
Download Telegram
Управление Docker-контейнерами с Python через docker-py

Когда проекты перестают помещаться в одну виртуалку, на сцену выходит Docker. Но почему бы не управлять контейнерами прямо из Python-скрипта, а не вручную через консоль? Для этого есть библиотека docker-py (официальный Docker SDK для Python).

---

### Установка и подключение

pip install docker


Простейшее подключение:

import docker

client = docker.from_env()


from_env() находит Docker-демон по переменным окружения (обычно это сокет /var/run/docker.sock).

---

### Запуск контейнера одной строкой

Запустим контейнер с nginx в фоне:

import docker

client = docker.from_env()

container = client.containers.run(
"nginx:latest",
detach=True,
ports={"80/tcp": 8080},
name="my_nginx"
)

print(container.id)


Что происходит:
- detach=True — контейнер не блокирует скрипт;
- ports пробрасывает 80 порт контейнера на 8080 хоста;
- name задает читаемое имя, чтобы не искать по ID.

---

### Список и состояние контейнеров

containers = client.containers.list(all=True)
for c in containers:
print(c.name, c.status)


Пара полезных статусов: running, exited, created.

Остановить и удалить контейнер по имени:

container = client.containers.get("my_nginx")
container.stop()
container.remove()


---

### Запуск команды внутри контейнера

Представьте, что нужно выполнить миграции или тесты внутри образа:

container = client.containers.run(
"python:3.12-slim",
command="python -c \"print('Hello from container')\"",
detach=True
)

logs = container.logs().decode("utf-8")
print(logs)
container.remove()


logs() возвращает вывод процесса — можно использовать для автоматизации CI/CD.

---

### Управление образами

Скачать образ, если его еще нет:

image = client.images.pull("redis:7-alpine")
print(image.tags)


Удалить неиспользуемый образ:

client.images.remove("redis:7-alpine")


---

### Где это пригодится

- написание своих mini-оркестраторов и утилит;
- автоматизация локальной разработки (запустить БД, брокер, кэш одной командой);
- тестирование в изолированном окружении с разными версиями Python и библиотек.

docker-py превращает Docker в обычную Python-библиотеку: вместо больших bash-скриптов — компактные и читаемые Python-программы.
3
- Как организовать загрузку данных с помощью многопоточного кода.
Python для начинающих: ускоряем загрузку данных с помощью многопоточности

Один файл скачать — не проблема. Десять — уже раздражает. Сто, тысяча — и ваш скрипт начинает жить своей жизнью, а вы смотрите, как он медленно тянет данные по одному запросу. Исправляем это с помощью многопоточности.

### Когда имеет смысл использовать потоки?

В Python потоки отлично подходят для задач, которые ждут сеть или диск, а не процессор:
— скачивание файлов;
— запросы к API;
— парсинг веб-страниц.

Ограничение GIL мешает распараллеливать чистые вычисления, но для сетевых операций потоки дают реальный прирост.

### Базовый вариант: requests + ThreadPoolExecutor

Сначала — наивный однопоточный код:

import requests

urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/3",
]

def fetch(url: str) -> str:
resp = requests.get(url, timeout=5)
return f"{url} -> {resp.status_code}"

for url in urls:
print(fetch(url))


Каждый запрос ждет завершения предыдущего. Время ≈ сумме задержек.

Теперь включим многопоточность:

from concurrent.futures import ThreadPoolExecutor, as_completed
import requests

urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/3",
]

def fetch(url: str) -> str:
resp = requests.get(url, timeout=5)
return f"{url} -> {resp.status_code}"

with ThreadPoolExecutor(max_workers=5) as executor:
future_to_url = {executor.submit(fetch, url): url for url in urls}
for future in as_completed(future_to_url):
print(future.result())


Что изменилось:

- ThreadPoolExecutor создает пул потоков.
- max_workers — максимальное число параллельных задач.
- executor.submit планирует выполнение функции fetch.
- as_completed позволяет обрабатывать результаты по мере готовности.

Теперь общее время будет примерно равно самому «длинному» запросу, а не сумме всех.

### Как выбрать max_workers?

Небольшое правило для сетевых задач:
max_workers ≈ 5–20 для начала.
Слишком мало — медленно. Слишком много — забьете сеть или получите лимиты от API.

### Скачивание и сохранение файлов

Минимальный пример, который реально полезен:

from concurrent.futures import ThreadPoolExecutor
import requests
from pathlib import Path

urls = [
"https://httpbin.org/image/png",
"https://httpbin.org/image/jpeg",
]

output_dir = Path("downloads")
output_dir.mkdir(exist_ok=True)

def download_file(url: str) -> str:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
filename = output_dir / url.split("/")[-1]
with open(filename, "wb") as f:
f.write(resp.content)
return f"Saved: {filename}"

with ThreadPoolExecutor(max_workers=4) as executor:
for result in executor.map(download_file, urls):
print(result)


Здесь executor.map удобен, когда нужно просто применить одну функцию ко множеству аргументов и собрать все результаты.

---

Многопоточность для загрузки данных — это простой способ превратить медленный последовательный скрипт в уверенный «пулевой поезд». Главное — использовать ее там, где программа ждет сеть, а не считает числа.
👍2
- Создание базового приложения для управления расписанием с визуализацией.
Создание базового приложения для управления расписанием с визуализацией

Когда задачи и встречи начинают жить собственной жизнью, пора заводить им цифровой «зоопарк» — небольшое приложение для расписания. Сделаем его на Python: будем хранить события, отображать их в виде таблицы и визуализировать нагрузку по дням.

### Шаг 1. Структура данных для расписания

Начнем с простой модели: список словарей, где каждое событие — это дата, время и описание.

from datetime import datetime

schedule = []

def add_event(date_str, time_str, title):
event_datetime = datetime.strptime(
f"{date_str} {time_str}", "%Y-%m-%d %H:%M"
)
schedule.append({
"datetime": event_datetime,
"title": title
})

add_event("2025-01-10", "09:00", "Morning meeting")
add_event("2025-01-10", "14:30", "Code review")
add_event("2025-01-11", "11:00", "Gym")


### Шаг 2. Текстовая «визуализация» в виде таблицы

Отсортируем события и красиво выведем их в консоли.

def print_schedule():
events_sorted = sorted(schedule, key=lambda e: e["datetime"])
print(f"{'Date':<12} {'Time':<6} Title")
print("-" * 40)
for e in events_sorted:
d = e["datetime"].strftime("%Y-%m-%d")
t = e["datetime"].strftime("%H:%M")
print(f"{d:<12} {t:<6} {e['title']}")

print_schedule()


Уже похоже на мини-органайзер, но хочется наглядности.

### Шаг 3. Визуализация загрузки по дням с matplotlib

Теперь посчитаем, сколько событий в каждый день, и построим столбчатую диаграмму. Для этого пригодится модуль collections и библиотека matplotlib.

from collections import Counter
import matplotlib.pyplot as plt

def plot_daily_load():
dates = [e["datetime"].date() for e in schedule]
counter = Counter(dates)

days = sorted(counter.keys())
counts = [counter[d] for d in days]

plt.bar([d.strftime("%Y-%m-%d") for d in days], counts, color="skyblue")
plt.xlabel("Date")
plt.ylabel("Events count")
plt.title("Schedule load by day")
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()

plot_daily_load()


Теперь видно, какие дни перегружены, а где можно добавить еще пару дел.

### Шаг 4. Куда развиваться дальше

Идеи для улучшения:

- сохранять расписание в json или sqlite3;
- добавлять фильтр по дате/ключевому слову;
- заменить консольный вывод на tkinter-интерфейс;
- выделять цветом важные события на графике.

Так из нескольких десятков строк кода получается живое мини-приложение, которое уже помогает управлять временем и показывает картину загрузки глазами Python.
👍5
Создание собственных итераторов и генераторов: как и зачем
Создание собственных итераторов и генераторов: как и зачем

Если вы поняли, как работает for в Python, вы уже на полпути к итераторам и генераторам. Осталось узнать, как сделать свою «ленту данных», которую можно перебирать по одному элементу — лениво, экономно и красиво.

---

### Итераторы: ручной режим

Итератор — это объект, у которого есть два метода: __iter__() и __next__().
for под капотом просто вызывает их.

Создадим свой итератор, который выдает квадраты чисел от 1 до n:

class Squares:
def __init__(self, n):
self.n = n
self.current = 1

def __iter__(self):
return self

def __next__(self):
if self.current > self.n:
raise StopIteration
value = self.current ** 2
self.current += 1
return value

for num in Squares(5):
print(num)


Плюсы: полный контроль — можно кешировать, логировать, считать статистику.
Минусы: много шаблонного кода.

---

### Генераторы: автоматический режим

Генератор — это «упрощенный итератор». Достаточно функции с yield, и Python сам создаст нужные методы.

def squares(n):
current = 1
while current <= n:
yield current ** 2
current += 1

for num in squares(5):
print(num)


Код компактнее, логика та же. Функция squares не возвращает сразу список, а выдает значения по одному — это экономит память.

---

### Где это полезно на практике?

1. Работа с большими данными

def read_lines(path):
with open(path, 'r', encoding='utf-8') as f:
for line in f:
yield line.rstrip('\n')


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

2. Бесконечные последовательности

def infinite_counter(start=0, step=1):
value = start
while True:
yield value
value += step


Такое нельзя сделать обычным списком — но генератор легко выдаёт «бесконечный» поток.

3. Пайплайны обработки

Генераторы удобно «цеплять» друг за другом:

def even_numbers(numbers):
for n in numbers:
if n % 2 == 0:
yield n

def squared(numbers):
for n in numbers:
yield n * n

data = range(10)
for num in squared(even_numbers(data)):
print(num)


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

---

### Итог

Итераторы дают вам каркас, генераторы — быстрый инструмент.
Если нужно точное поведение и сложное состояние — пишите свой класс-итератор.
Если хотите простой, читаемый и ленивый поток данных — используйте генераторы.

Понимание этих двух механизмов превращает for из «магии» в управляемый инструмент, с которым легко строить эффективные и элегантные конструкции в Python.
👍3
Как использовать модуль itertools для сложных операций с коллекциями
Как использовать модуль itertools для сложных операций с коллекциями

Модуль itertools — это как швейцарский нож для работы с последовательностями. Многие задачи, которые обычно решают вложенными циклами и временными списками, здесь превращаются в одну-две строки кода.

---

### Комбинаторика без боли: product, permutations, combinations

from itertools import product, permutations, combinations

colors = ["red", "green"]
sizes = ["S", "M", "L"]

# Декартово произведение: все сочетания цвета и размера
for item in product(colors, sizes):
print(item)
# ('red', 'S'), ('red', 'M'), ...

digits = [1, 2, 3]

# Все перестановки длиной 3
print(list(permutations(digits, 3))) # порядок важен

# Все комбинации по 2 элемента
print(list(combinations(digits, 2))) # порядок не важен


product идеально подходит для перебора параметров в тестах или конфигурациях, а permutations и combinations — для задач по перебору вариантов (пароли, маршруты, наборы).

---

### Цепочки и бесконечные последовательности: chain, count, cycle

from itertools import chain, count, cycle, islice

a = [1, 2]
b = [3, 4]

# Склеивание нескольких коллекций
for x in chain(a, b, [5, 6]):
print(x)

# Бесконечный счетчик + ограничение через islice
for n in islice(count(10, 2), 5):
print(n) # 10, 12, 14, 16, 18

# Циклический перебор
colors = ["red", "green", "blue"]
for color in islice(cycle(colors), 7):
print(color)


chain удобен, когда не хочется создавать новый список через +. count и cycle дают бесконечные последовательности, но с ними almost всегда используют islice, чтобы не зациклиться навсегда.

---

### Группировка и фильтрация: groupby, compress

from itertools import groupby, compress

data = "aaabbbccccdd"
for char, group in groupby(data):
print(char, len(list(group))) # символ и сколько раз подряд встретился

items = ["apple", "banana", "cherry", "date"]
selectors = [1, 0, 1, 0]

# Выбираем только те элементы, где selector == 1
print(list(compress(items, selectors))) # ['apple', 'cherry']


groupby удобно использовать для сжатия повторов или простой аналитики. compress — это фильтрация по маске из True/False (или 1/0).

---

### Комбинаторика на стероидах: combinations_with_replacement

from itertools import combinations_with_replacement

coins = [1, 2, 5]
for combo in combinations_with_replacement(coins, 3):
print(combo)


Можно перебирать комбинации с повторениями — полезно для задач типа «как разменять сумму монетами».

---

itertools позволяет писать компактный, но мощный код для сложных операций с коллекциями, не утопая в вложенных циклах и временных списках. Чем больше вы его используете, тем чаще ловите себя на мысли: «Так, это же удобно решить одной функцией из itertools».
👍2🔥1
Понимание и применение генераторов: экономия памяти с yield
Понимание и применение генераторов: экономия памяти с yield

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

---

### Что такое генератор?

Генератор — это «ленивый» источник данных. Он не хранит все значения сразу, а выдает их по одному по мере запроса. Вместо return в функции используется yield.

def count_up_to(n):
current = 1
while current <= n:
yield current
current += 1

gen = count_up_to(5)
for num in gen:
print(num)


Здесь не создается список [1, 2, 3, 4, 5]. В каждый момент в памяти только одно число.

---

### Генераторы против списков: почему это экономично

# список
squares_list = [i * i for i in range(10_000_000)]

# генератор
squares_gen = (i * i for i in range(10_000_000))


squares_list реально занимает память под 10 млн чисел.
squares_gen — это объект, который умеет по запросу выдавать квадрат очередного числа. Его размер в памяти — примерно как у обычного небольшого объекта, независимо от диапазона.

Используйте генератор, когда:

- данные большие или потенциально бесконечные;
- вы проходите по последовательности один раз;
- вам не нужно случайно обращаться к элементу по индексу.

---

### Практический пример: чтение большого файла

def read_large_file(path):
with open(path, 'r', encoding='utf-8') as f:
for line in f:
yield line.strip()

for line in read_large_file('logs.txt'):
if 'ERROR' in line:
print(line)


Мы не загружаем весь файл в память, а читаем его построчно. Для логов, дампов и CSV на гигабайты — это буквально спасение.

---

### Комбинирование генераторов

Генераторы отлично «цепляются» друг за друга:

def even_numbers(numbers):
for n in numbers:
if n % 2 == 0:
yield n

nums = (i for i in range(100)) # генераторное выражение
evens = even_numbers(nums) # генератор-фильтр

for n in evens:
print(n)


Каждый шаг выдает только один элемент: минимум памяти, максимум гибкости.

---

### Главное запомнить

- yield превращает функцию в генератор.
- Генераторы не хранят все данные, а создают их «по требованию».
- Это критично для работы с большими файлами, потоками данных и длинными диапазонами.

Как только ловите себя на том, что создаете огромный список «просто чтобы по нему один раз пройтись» — подумайте: а не пора ли заменить его на генератор?
👍2🔥21
Работа с коллекциями: полезные структуры данных из модуля collections
Работа с коллекциями: полезные структуры данных из collections

Стандартный список и словарь решают 80% задач. Модуль collections помогает закрыть остальные 20% — красиво, быстро и без велосипеда. Разберём самые полезные структуры: Counter, defaultdict, deque и namedtuple.

---

### 1. Counter: кто здесь самый частый?

Counter считает, сколько раз элемент встретился в последовательности.

from collections import Counter

text = "banana apple banana orange apple banana"
words = text.split()

freq = Counter(words)
print(freq) # Counter({'banana': 3, 'apple': 2, 'orange': 1})
print(freq.most_common(1)) # [('banana', 3)]


Удобен для анализа логов, текста, статистики кликов и т.д.

---

### 2. defaultdict: словарь, который не ругается

Обычный словарь выбросит ошибку, если обратиться к несуществующему ключу. defaultdict сам создаст значение по умолчанию.

from collections import defaultdict

scores = [("Alice", 10), ("Bob", 5), ("Alice", 7)]

user_scores = defaultdict(list)
for name, score in scores:
user_scores[name].append(score)

print(user_scores) # {'Alice': [10, 7], 'Bob': [5]}


Отлично подходит для группировки данных.

---

### 3. deque: быстрый список с двух концов

list медленный для вставки/удаления в начале. deque (double-ended queue) делает это за O(1).

from collections import deque

queue = deque()

queue.append("task_1")
queue.append("task_2")
queue.appendleft("urgent_task")

print(queue) # deque(['urgent_task', 'task_1', 'task_2'])

queue.pop() # убрали с конца
queue.popleft() # убрали с начала


Используется для очередей, буферов, слайдинговых окон.

---

### 4. namedtuple: читаемые кортежи

Обычный кортеж — это item[0], item[1] и постоянное забывание, где что. namedtuple даёт имена полям.

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)

print(p.x, p.y) # 3 4
print(p[0], p[1]) # тоже работает


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

---

Модуль collections — это маленький апгрейд стандартных структур, который сильно упрощает жизнь. Освоив эти четыре инструмента, вы уже будете писать более чистый и эффективный код, чем большинство начинающих.
🔥4
Как работать с типизированными данными с помощью модуля typing
Как подружиться с типами в Python с помощью typing

Python динамический, но иногда это слишком большая свобода. Функция ждёт число, а получает словарь, и ошибка вылезает только в рантайме. Модуль typing помогает заранее описывать, что именно мы ожидаем, и ловить часть ошибок ещё до запуска — с помощью статических анализаторов (например, mypy, PyCharm, VS Code).

---

### Базовые аннотации

from typing import List, Dict

def average(values: List[float]) -> float:
return sum(values) / len(values)

def word_count(texts: List[str]) -> Dict[str, int]:
result: Dict[str, int] = {}
for t in texts:
result[t] = result.get(t, 0) + 1
return result


Здесь мы явно говорим: values — список чисел с плавающей точкой, word_count возвращает словарь слово -> количество.

---

### Optional и Union: когда вариантов несколько

from typing import Optional, Union

def to_int(value: Union[str, float, int]) -> Optional[int]:
try:
return int(value)
except (TypeError, ValueError):
return None


Union означает: параметр может быть одним из перечисленных типов. Optional[int] — просто сокращение для Union[int, None].

---

### Собственные типы с TypedDict

Если вы любите словари, но хотите немного порядка:

from typing import TypedDict

class User(TypedDict):
id: int
name: str
is_active: bool

def greet(user: User) -> str:
status = "active" if user["is_active"] else "inactive"
return f"{user['name']} ({status})"


Теперь статический анализатор предупредит, если вы забудете, например, is_active или передадите строку вместо int для id.

---

### Protocol: утка по правилам

Иногда важен не тип объекта, а то, какие у него есть методы:

from typing import Protocol

class Writer(Protocol):
def write(self, data: str) -> None:
...

def log_message(writer: Writer, message: str) -> None:
writer.write(message + "\n")


Любой объект с методом write(str) -> None подойдёт: файл, буфер, ваш собственный логгер. Это структурная типизация.

---

### Обобщения с TypeVar

Хотите одну функцию для разных типов, но с сохранением информации о них?

from typing import TypeVar, List

T = TypeVar("T")

def first(items: List[T]) -> T:
return items[0]


Если передать List[int], тип результата будет int, а не «что-то неопределённое».

---

Модуль typing не делает код быстрее и не защищает от всех ошибок, но даёт мощный инструмент: формализовать ваши ожидания от данных. А чем точнее формулировки, тем меньше сюрпризов в рантайме.
Создание и экспорт PDF-файлов с помощью библиотеки ReportLab
Создание и экспорт PDF-файлов с помощью ReportLab

PDF любят бухгалтеры, юристы и вообще все, кому важен аккуратный документ “как из принтера”. А Python это тоже умеет — с помощью библиотеки ReportLab.

---

### Установка

pip install reportlab


После этого можно собирать PDF буквально по строчкам.

---

### Самый простой PDF

Создадим файл hello.pdf с одной строкой текста:

from reportlab.pdfgen import canvas

def create_simple_pdf(filename):
c = canvas.Canvas(filename)
c.drawString(100, 750, "Hello, ReportLab!")
c.save()

create_simple_pdf("hello.pdf")


Пара моментов:

- Координаты считаются в поинтах (1/72 дюйма), (0, 0) — левый нижний угол.
- save() обязателен, иначе файл будет пустым.

---

### Добавляем шрифт и несколько строк

Можно управлять шрифтами и цветами:

from reportlab.pdfgen import canvas
from reportlab.lib.colors import blue, red

def create_styled_pdf(filename):
c = canvas.Canvas(filename)

c.setFont("Helvetica-Bold", 20)
c.setFillColor(blue)
c.drawString(100, 780, "Invoice #42")

c.setFont("Helvetica", 12)
c.setFillColor(red)
c.drawString(100, 750, "Customer: John Smith")
c.drawString(100, 730, "Total: $199.99")

c.save()

create_styled_pdf("invoice.pdf")


Так уже можно генерировать простые счета, акты, сертификаты.

---

### Несколько страниц

PDF — не только один лист:

from reportlab.pdfgen import canvas

def create_multipage_pdf(filename):
c = canvas.Canvas(filename)

for page_num in range(1, 4):
c.drawString(100, 750, f"Page {page_num}")
c.showPage() # переход на новую страницу

c.save()

create_multipage_pdf("multipage.pdf")


Каждый вызов showPage() завершает текущую страницу и начинает следующую.

---

### Таблица с использованием platypus

Для отчетов удобен модуль platypus (он входит в ReportLab):

from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
from reportlab.lib import colors

def create_table_pdf(filename):
doc = SimpleDocTemplate(filename)
data = [
["Name", "Quantity", "Price"],
["Apples", 3, "$5"],
["Oranges", 5, "$7"],
["Bananas", 2, "$4"],
]

table = Table(data)
table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey),
("GRID", (0, 0), (-1, -1), 0.5, colors.black),
("ALIGN", (1, 1), (-1, -1), "CENTER"),
]))

doc.build([table])

create_table_pdf("table_report.pdf")


Так уже получается вполне рабочий отчет: заголовки, сетка, выравнивание.

---

ReportLab — мощный “принтер” для Python: формировать счета, договоры, пропуска, сертификаты можно автоматически, просто подставляя данные. Скрипт запускается — и через секунду готов аккуратный PDF, который не стыдно отправить клиенту.
👍1
Основы сериализации объектов с использованием pickle и json
Python для начинающих: основы сериализации с pickle и json

Представьте, что вы написали класс, настроили кучу объектов, а программу нужно закрыть. Как сохранить их состояние, чтобы потом просто "разморозить" и продолжить? Здесь вступает в игру сериализация — превращение объекта в последовательность байт или строку, которую можно записать в файл или отправить по сети.

В Python для этого чаще всего используют два модуля: pickle и json. Они решают похожую задачу, но по-разному.

---

## pickle: все из Python — для Python

pickle умеет сериализовать почти любые Python-объекты: списки, словари, функции, пользовательские классы.

import pickle

class User:
def __init__(self, name, score):
self.name = name
self.score = score

user = User("Alice", 42)

# Сериализация в байты
data = pickle.dumps(user)

# Запись в файл
with open("user.pkl", "wb") as f:
pickle.dump(user, f)

# Чтение из файла
with open("user.pkl", "rb") as f:
loaded_user = pickle.load(f)

print(loaded_user.name, loaded_user.score) # Alice 42


Плюсы:
- поддерживает "почти всё" из мира Python;
- минимум кода.

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

---

## json: читаемо, переносимо, безопаснее

json сериализует данные в текстовый формат, понятный другим языкам (JavaScript, Go, Java и т.д.). Но он понимает только простые типы: dict, list, str, int, float, bool, None.

import json

user = {
"name": "Alice",
"score": 42,
"tags": ["python", "beginner"]
}

# В строку
json_str = json.dumps(user)

# В файл
with open("user.json", "w", encoding="utf-8") as f:
json.dump(user, f, indent=4)

# Из файла
with open("user.json", "r", encoding="utf-8") as f:
loaded_user = json.load(f)

print(loaded_user["name"], loaded_user["tags"])


Если нужно сериализовать объект своего класса, его придётся "разобрать" в словарь:

class User:
def __init__(self, name, score):
self.name = name
self.score = score

def user_to_dict(user):
return {"name": user.name, "score": user.score}

user = User("Bob", 100)
json_str = json.dumps(user_to_dict(user))


---

## Что когда использовать?

- Нужна максимальная совместимость, читаемый текст и обмен с другими языками — берите json.
- Нужна быстрая работа с "нативными" Python-объектами и файл не покидает ваш сервис — подойдёт pickle, но помните о безопасности.

Сериализация — это способ дать вашим объектам "вторую жизнь" после завершения программы. Освоив pickle и json, вы уже делаете шаг в сторону реальных приложений: конфиги, кэш, сохранения состояния и обмен данными.
👍21
Как использовать модуль time для измерения скорости выполнения