Python для начинающих
1.24K subscribers
546 photos
3 videos
232 files
74 links
Python для начинающих
Download Telegram
Как использовать collections.Counter для простого анализа данных
Как использовать collections.Counter для простого анализа данных

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

---

### Что такое Counter

Counter — это специальный словарь для подсчёта количества элементов. Он считает, сколько раз встречается каждый объект, и делает это очень удобно.

from collections import Counter

data = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
counter = Counter(data)

print(counter) # Counter({'apple': 3, 'banana': 2, 'orange': 1})
print(counter['apple']) # 3


Counter сразу выдаёт вам частоты элементов. Можно думать о нём как об «умном» словаре для статистики.

---

### Анализ текста: частота слов и букв

Классический пример — быстрый анализ текста.

from collections import Counter

text = "data analysis with python and data tools"
words = text.split()

word_freq = Counter(words)
char_freq = Counter(text.replace(" ", ""))

print(word_freq.most_common(3)) # топ-3 самых частых слов
print(char_freq.most_common(5)) # топ-5 самых частых символов


Метод .most_common(n) возвращает n самых популярных элементов — готовая мини-аналитика.

---

### Подсчёт категорий: простой пример «аналитики»

Допустим, у вас есть список заказов с типами товаров:

from collections import Counter

orders = [
"book", "book", "laptop", "phone",
"book", "phone", "tablet", "laptop"
]

category_stats = Counter(orders)

print(category_stats) # сколько каких товаров заказали
print(category_stats.most_common(1)) # самый популярный товар


Без циклов и лишнего кода вы уже понимаете распределение по категориям.

---

### Операции над Counter: как с многомерными счётчиками

Counter умеет складывать, вычитать и объединять данные — удобно для сравнения наборов.

from collections import Counter

week1 = Counter(['book', 'book', 'phone'])
week2 = Counter(['book', 'tablet', 'phone', 'phone'])

print(week1 + week2) # суммарные продажи
print(week2 - week1) # что «добавилось» во второй неделе


Так можно быстро сравнивать периоды, версии данных или сегменты пользователей.

---

### Фильтрация и преобразование результата

Counter легко превратить в обычный словарь и отфильтровать по условию:

from collections import Counter

data = ['a', 'b', 'a', 'c', 'b', 'a', 'd']
c = Counter(data)

freq = {k: v for k, v in c.items() if v > 1}
print(freq) # {'a': 3, 'b': 2}


---

collections.Counter — отличный инструмент, чтобы «понюхать данные»: быстро понять, что в них чаще всего встречается, найти лидеров и выбросы. И всё это в пару строк кода, без тяжёлых библиотек и сложной математики.
👍3
Создание PDF документов с помощью библиотеки FPDF
Создание PDF-документов с помощью FPDF: просто, как print()

PDF — идеальный формат для чеков, отчетов, сертификатов и даже мини-отчетов по учебным проектам. В Python один из самых понятных инструментов для их создания — библиотека fpdf2 (современный форк FPDF).

---

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

pip install fpdf2


Базовый объект — FPDF, с ним и будем работать.

---

### Первый PDF за 10 строк

Создадим простой одностраничный документ с заголовком и текстом.

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", size=16)
pdf.cell(0, 10, "Hello, PDF world!", ln=1, align="C")

pdf.set_font("Arial", size=12)
text = "This is a simple PDF generated with fpdf2 in Python."
pdf.multi_cell(0, 8, text)

pdf.output("example_basic.pdf")


Ключевые моменты:
- add_page() — добавляем страницу.
- set_font() — выбираем шрифт и размер.
- cell() — строка текста; ln=1 переносит курсор на новую строку.
- multi_cell() — блок текста с переносами.
- output() — сохраняем файл.

---

### Простой “отчет”: таблица в PDF

Сделаем мини-таблицу, например, список задач.

from fpdf import FPDF

data = [
("ID", "Task", "Status"),
("1", "Learn FPDF basics", "Done"),
("2", "Generate simple report", "In progress"),
("3", "Share PDF with team", "Pending"),
]

pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", "B", 14)
pdf.cell(0, 10, "Tasks Report", ln=1, align="C")
pdf.ln(4)

pdf.set_font("Arial", size=11)
col_widths = [15, 90, 30]

for row_index, row in enumerate(data):
for col_index, text in enumerate(row):
style = "B" if row_index == 0 else ""
pdf.set_font("Arial", style, 11)
pdf.cell(col_widths[col_index], 8, text, border=1)
pdf.ln(8)

pdf.output("tasks_report.pdf")


Здесь:
- Используем список кортежей как источник данных.
- Меняем стиль шрифта для заголовка таблицы.
- border=1 рисует рамки ячеек — уже похоже на отчет.

---

### Добавление изображения

Например, логотип в шапке:

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.image("logo.png", x=10, y=8, w=30)

pdf.set_font("Arial", "B", 16)
pdf.cell(0, 10, "Company Report", ln=1, align="C")

pdf.output("report_with_logo.pdf")


---

FPDF удобна тем, что работает “как конструктор”: просто добавляете текст, таблицы, картинки, постепенно превращая Python-скрипт в генератор аккуратных PDF-документов. Отличный навык для автоматизации отчетов и учебных проектов.
👍31
Введение в работу с INI файлами с модулем configparser
Введение в работу с INI‑файлами с модулем configparser

Когда настройки программы начинают расползаться по коду в виде "магических констант", наступает хаос. Гораздо удобнее хранить конфиг в отдельном файле — компактно, читаемо и без лишних зависимостей. Для этого в Python есть стандартный модуль configparser, который работает с INI‑файлами.

### Как выглядит INI‑файл

Типичный settings.ini:

[database]
host = localhost
port = 5432
user = admin

[app]
debug = true
log_level = INFO


Секции в квадратных скобках, ниже — пары ключ = значение.

### Чтение INI с configparser

import configparser

config = configparser.ConfigParser()
config.read("settings.ini")

db_host = config["database"]["host"]
db_port = config.getint("database", "port")
debug_mode = config.getboolean("app", "debug")

print(db_host, db_port, debug_mode)


Полезные методы:
- get() — строка (по умолчанию)
- getint(), getfloat(), getboolean() — автоматическое приведение типов
- квадратные скобки config["section"]["key"] — быстрый доступ как к словарю

### Значения по умолчанию

Если нужного ключа нет — можно задать дефолт:

log_level = config.get("app", "log_level", fallback="WARNING")


Так код не упадет, даже если в INI забыли прописать опцию.

### Создание и запись INI‑файла

import configparser

config = configparser.ConfigParser()

config["database"] = {
"host": "localhost",
"port": "3306",
"user": "root"
}

config["app"] = {}
config["app"]["debug"] = "false"
config["app"]["log_level"] = "INFO"

with open("generated.ini", "w") as config_file:
config.write(config_file)


Секции и ключи ведут себя почти как обычные словари.

### Переменные и интерполяция

configparser умеет подставлять значения из других опций:

[paths]
base_dir = /var/app
logs_dir = %(base_dir)s/logs


import configparser

config = configparser.ConfigParser()
config.read("paths.ini")

logs_dir = config["paths"]["logs_dir"]
print(logs_dir) # /var/app/logs


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

config = configparser.ConfigParser(interpolation=None)


---

configparser — простой способ вынести настройки из кода, не таща в проект JSON/YAML и сторонние библиотеки. Для маленьких и средних приложений INI‑файл нередко оказывается приятным и достаточным форматом конфигурации.
👍3
Создание простого чат-сервера с использованием сокетов
Создаем простой чат-сервер на сокетах: от нуля до мини-мессенджера

Сокеты — это базовый способ общения программ по сети. Без них не было бы ни браузеров, ни мессенджеров, ни онлайн-игр. Давай сделаем свой простой чат-сервер на Python и поймем, как это работает изнутри.

---

## Базовая идея

Нам нужны две части:

1. Сервер — ждет подключения клиентов и пересылает сообщения.
2. Клиент — подключается к серверу и отправляет/получает сообщения.

Используем стандартный модуль socket, ничего дополнительно устанавливать не нужно.

---

## Простой сервер

Этот сервер:
- принимает несколько клиентов,
- читает от каждого сообщения,
- рассылает их всем остальным.

import socket
import threading

HOST = "127.0.0.1"
PORT = 5000

clients = []

def broadcast(message, sender_sock):
for client in clients:
if client is not sender_sock:
try:
client.sendall(message)
except OSError:
pass

def handle_client(client_sock, addr):
print(f"Connected: {addr}")
while True:
try:
data = client_sock.recv(1024)
if not data:
break
broadcast(data, client_sock)
except ConnectionResetError:
break
print(f"Disconnected: {addr}")
clients.remove(client_sock)
client_sock.close()

def run_server():
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_sock.bind((HOST, PORT))
server_sock.listen()
print(f"Server listening on {HOST}:{PORT}")

while True:
client_sock, addr = server_sock.accept()
clients.append(client_sock)
thread = threading.Thread(target=handle_client, args=(client_sock, addr), daemon=True)
thread.start()

if __name__ == "__main__":
run_server()


Ключевые моменты:
- socket.AF_INET, socket.SOCK_STREAM — TCP-соединение по IPv4.
- listen() — сервер готов принимать соединения.
- Для каждого клиента создаем поток, чтобы они не блокировали друг друга.

---

## Простой клиент

Подключаемся к серверу и читаем/пишем сообщения одновременно.

import socket
import threading

HOST = "127.0.0.1"
PORT = 5000

def receive_messages(sock):
while True:
try:
data = sock.recv(1024)
if not data:
print("Connection closed by server")
break
print(">>", data.decode("utf-8"))
except OSError:
break

def run_client():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
print("Connected to chat server")

thread = threading.Thread(target=receive_messages, args=(sock,), daemon=True)
thread.start()

try:
while True:
msg = input()
if msg.lower() == "/quit":
break
sock.sendall(msg.encode("utf-8"))
finally:
sock.close()

if __name__ == "__main__":
run_client()


---

## Что можно улучшить дальше

- Добавить никнеймы и формат сообщений ([user]: text).
- Логировать чаты в файл.
- Использовать asyncio вместо потоков для масштабируемости.

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

Когда слышишь «графы», кажется, что это что‑то из теории алгоритмов для олимпиадников. Но в Python уже есть готовый инструмент для работы с простыми графами — модуль graphlib, который появился в версии 3.9. И использовать его можно прямо «из коробки», без сторонних библиотек.

### Что за модуль graphlib?

В graphlib есть класс TopologicalSorter. Он работает с ориентированными ациклическими графами (DAG) и умеет:

- проверять, есть ли циклы;
- выполнять топологическую сортировку (определять порядок выполнения задач с зависимостями);
- пошагово «выстреливать» вершины, которые уже можно обработать.

Граф здесь задаётся как зависимости: указываем, какие вершины зависят от каких.

### Простейший пример: порядок задач

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

from graphlib import TopologicalSorter

graph = {
"cook_pasta": {"buy_products"},
"make_sauce": {"buy_products"},
"serve_dinner": {"cook_pasta", "make_sauce"},
"buy_products": set(),
}

ts = TopologicalSorter(graph)
order = list(ts.static_order())
print(order)


Вывод будет чем‑то вроде:

['buy_products', 'cook_pasta', 'make_sauce', 'serve_dinner']


Мы получили корректный порядок выполнения без единого if и хитрых циклов.

Важно: если в графе есть цикл, static_order() выбросит CycleError. Это удобно для проверки зависимостей.

### Пошаговая обработка: имитация планировщика

TopologicalSorter можно использовать как мини‑движок для планировщика задач:

from graphlib import TopologicalSorter

graph = {
"download_data": set(),
"clean_data": {"download_data"},
"train_model": {"clean_data"},
"evaluate_model": {"train_model"},
}

ts = TopologicalSorter(graph)
ts.prepare()

while ts.is_active():
ready_tasks = ts.get_ready()
for task in ready_tasks:
print(f"Running: {task}")
# Здесь могла бы быть реальная работа
ts.done(task)


Методы:

- prepare() — инициализация процесса;
- get_ready() — вернуть все вершины, у которых уже выполнены зависимости;
- done(node) — сообщить, что вершина обработана;
- is_active() — есть ли ещё работа.

Так можно, например, отправлять независимые задачи в разные потоки или процессы.

### Мини‑валидатор зависимостей

Ещё один практичный сценарий — проверка корректности конфигураций:

from graphlib import TopologicalSorter, CycleError

def validate_dependencies(dep_graph: dict) -> bool:
try:
TopologicalSorter(dep_graph).static_order()
return True
except CycleError as e:
print("Cycle detected:", e)
return False

deps_ok = {
"A": {"B"},
"B": {"C"},
"C": set(),
}

deps_bad = {
"A": {"B"},
"B": {"C"},
"C": {"A"},
}

print(validate_dependencies(deps_ok)) # True
print(validate_dependencies(deps_bad)) # False + сообщение о цикле


Так всего парой строк можно защититься от «замкнувшихся» настроек.

---

graphlib — отличный вход в мир графов: минимум теории, максимум практики. Вы просто описываете зависимости, а Python берёт на себя сложную часть — поиск порядка и ловлю циклов.
👍3🔥1
Создание временного файла с модулем tempfile
Создание временного файла с модулем tempfile

Работа с файлами — обычное дело в Python: логи, отчёты, экспорт данных. Но часто файл нужен «на пару секунд» — что‑то записать, передать другой функции, а потом забыть. Создавать для этого реальные файлы в папке проекта неудобно и небезопасно. Здесь на сцену выходит модуль tempfile.

tempfile отвечает за создание временных файлов и папок в системном временном каталоге (подбирается автоматически под вашу ОС). Его главный плюс — безопасность и автоматическая уборка за собой.

---

### Простейший временный файл

Функция NamedTemporaryFile создаёт временный файл с именем, к которому можно обратиться как к обычному файлу:

import tempfile

with tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".txt") as tmp_file:
tmp_file.write("Some temporary data\n")
tmp_file.flush() # сбрасываем буфер на диск

print("Temp file name:", tmp_file.name)

tmp_file.seek(0)
content = tmp_file.read()
print("Content:", content)

# Здесь файл уже удалён автоматически


Ключевые моменты:

- mode="w+" — можно и писать, и читать.
- delete=True — файл будет удалён при выходе из with.
- suffix=".txt" — можно задать расширение (полезно для сторонних программ, ожидающих конкретный тип файла).
- tmp_file.name — реальный путь к файлу на диске.

---

### Временный файл без имени

Если имя файла не нужно (например, вы просто временно храните данные в пределах одного процесса), используйте TemporaryFile:

import tempfile

with tempfile.TemporaryFile(mode="w+") as tmp:
tmp.write("Internal buffer\n")
tmp.seek(0)
print(tmp.read())


Такой файл может вообще не иметь «нормального» имени в файловой системе (зависит от ОС), зато всё ещё ведёт себя как обычный файловый объект.

---

### Где хранятся временные файлы?

Посмотреть каталог, который использует tempfile, можно так:

import tempfile
print(tempfile.gettempdir())


Обычно это что‑то вроде /tmp на Linux или C:\Users\<user>\AppData\Local\Temp на Windows.

---

### Зачем это нужно на практике?

- Генерация отчёта, который тут же отправляется по сети.
- Временное сохранение загруженного файла перед обработкой.
- Тестирование кода, работающего с файловой системой, без мусора в проекте.

tempfile позволяет писать код, который не оставляет следов, не конфликтует с существующими файлами и не требует ручной уборки. Для аккуратных Python‑разработчиков — must‑have инструмент.
👍2
Изучение интерфейса для очередей очередей с queue.Queue
Изучаем очередь задач с queue.Queue: безопасный обмен данными между потоками

Когда в программе появляется несколько потоков, один из первых вопросов — как безопасно передавать им данные. Глобальные списки и словари быстро превращают код в хаос. Для таких задач в стандартной библиотеке есть герой попроще и понадежнее — queue.Queue.

---

## Что такое queue.Queue?

Queue — это потокобезопасная структура данных «первым пришёл — первым вышел» (FIFO).
Главные свойства:

- безопасна для работы из нескольких потоков;
- умеет блокироваться при put() и get(), пока не появится место или элемент;
- поддерживает ограничение размера (maxsize), чтобы не «забить» память.

Подключается просто:

from queue import Queue


---

## Базовый пример: очередь задач

Допустим, у нас есть «производитель» задач и «потребитель», который их обрабатывает:

from queue import Queue
from threading import Thread
import time

def producer(task_queue, n_tasks):
for i in range(n_tasks):
task = f"task_{i}"
print(f"Produce: {task}")
task_queue.put(task) # блокируется, если очередь заполнена
task_queue.put(None) # сигнал завершения

def consumer(task_queue):
while True:
task = task_queue.get() # блокируется, пока очередь пуста
if task is None: # получили сигнал "конец"
task_queue.task_done()
break
print(f"Consume: {task}")
time.sleep(0.2) # имитация работы
task_queue.task_done()

def main():
task_queue = Queue(maxsize=5)

t_prod = Thread(target=producer, args=(task_queue, 10))
t_cons = Thread(target=consumer, args=(task_queue,))

t_prod.start()
t_cons.start()

task_queue.join() # ждём, пока все задачи будут обработаны
t_prod.join()
t_cons.join()

if __name__ == "__main__":
main()


Ключевые моменты:

- put() и get() по умолчанию блокирующие;
- task_done() говорит очереди: «элемент обработан»;
- join() блокируется, пока количество task_done() не сравняется с количеством put().

---

## Неблокирующий режим и таймауты

Иногда блокироваться нельзя:

from queue import Queue, Empty, Full

q = Queue(maxsize=2)

try:
q.put("item1", block=False)
q.put("item2", block=False)
q.put("item3", timeout=0.5) # подождём немного
except Full:
print("Queue is full!")

try:
item = q.get(block=False)
print("Got:", item)
item = q.get(timeout=0.5)
except Empty:
print("Queue is empty!")


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

---

## Когда использовать queue.Queue

- Обработка задач в нескольких потоках (скачивание файлов, парсинг страниц).
- Логирование из разных потоков в один обработчик.
- Поток-производитель (сбор данных) и поток-потребитель (анализ, запись в БД).

Главный плюс: Queue берет на себя всю головную боль с блокировками и синхронизацией. Вам остается думать о логике задач, а не о том, как не устроить гонку данных и дедлок.
👍2🔥1
Создание zip-архива и добавление файлов средствами shutil и zipfile
Создание zip-архива и добавление файлов средствами shutil и zipfile

Работа с архивами — один из тех навыков, которые рано или поздно нужны любому Python-разработчику. Сделать резервную копию проекта, упаковать отчёты, автоматически отправить архив по почте — всё это удобно делать прямо из кода. В стандартной библиотеке есть два ключевых инструмента: shutil и zipfile. Разберём оба.

---

## Вариант 1: Быстрое сжатие папки через shutil

shutil хорош, когда нужно просто взять каталог и превратить его в zip-архив, не вдаваясь в детали.

import shutil
from pathlib import Path

base_dir = Path("project_data")
archive_name = "project_backup" # без расширения

# Создаст файл project_backup.zip с содержимым папки project_data
archive_path = shutil.make_archive(
base_name=archive_name,
format="zip",
root_dir=base_dir
)

print(f"Archive created: {archive_path}")


Особенности:
- Архивируется вся папка целиком.
- Почти нет настроек: минимум кода, максимум результата.
- Удобно для периодических бэкапов и упаковки готовых проектов.

---

## Вариант 2: Тонкий контроль через zipfile

Когда нужно добавлять файлы по одному, менять путь внутри архива, докладывать файлы позже — пригодится zipfile.

### Создание архива и добавление файлов

import zipfile
from pathlib import Path

zip_path = Path("reports.zip")

files_to_add = [
Path("reports/january.csv"),
Path("reports/february.csv"),
]

with zipfile.ZipFile(zip_path, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
for file_path in files_to_add:
arc_name = file_path.name # как файл будет называться внутри архива
zf.write(file_path, arcname=arc_name)

print(f"Created: {zip_path}")


### Дозапись файлов в существующий архив

import zipfile
from pathlib import Path

zip_path = Path("reports.zip")

with zipfile.ZipFile(zip_path, mode="a", compression=zipfile.ZIP_DEFLATED) as zf:
zf.write("reports/march.csv", arcname="march.csv")


---

## Чтение и извлечение содержимого

import zipfile

with zipfile.ZipFile("reports.zip", mode="r") as zf:
print("Files in archive:")
for name in zf.namelist():
print(" -", name)

# Извлечь всё в папку extracted_reports
zf.extractall("extracted_reports")


---

Когда что использовать?

- shutil.make_archive — когда нужно быстро заархивировать каталог целиком.
- zipfile.ZipFile — когда нужен контроль: выборочные файлы, особые имена внутри архива, дозапись, чтение без распаковки.

Оба модуля входят в стандартную библиотеку, так что никаких дополнительных установок — только импорт и немного аккуратного кода.
👍3
Работа со временем: tzinfo и pytz для часовых поясов
Работа со временем в Python: tzinfo и pytz без магии и боли

Если вы хоть раз пытались работать с часовыми поясами, вы знаете: это ад из смещений, переходов на летнее время и странных правил разных стран. Но Python умеет в это неплохо — если правильно пользоваться tzinfo и библиотекой pytz.

---

### Наивные и осознанные datetime

По умолчанию datetime в Python не знает, в каком он часовом поясе.

from datetime import datetime

dt_naive = datetime(2024, 4, 5, 12, 0, 0)
print(dt_naive.tzinfo) # None


Это наивный объект — он не привязан ни к одному поясу. Любая арифметика и сравнения с другими датами могут быть некорректны, если вы смешиваете разные пояса.

---

### Интерфейс tzinfo

Класс tzinfo — это абстракция часового пояса. В теории вы можете написать свой класс, унаследованный от tzinfo, который определит:

- utcoffset() — смещение от UTC
- dst() — переход на летнее время
- tzname() — имя пояса

Но на практике руками это почти никогда не делают: слишком много нюансов. Поэтому используется pytz.

---

### Подключаем pytz

Устанавливаем:

pip install pytz


Простой пример: берём локальное время в Москве и переводим его в Нью-Йорк.

from datetime import datetime
import pytz

tz_moscow = pytz.timezone("Europe/Moscow")
tz_ny = pytz.timezone("America/New_York")

dt_naive = datetime(2024, 4, 5, 12, 0, 0)

dt_moscow = tz_moscow.localize(dt_naive)
dt_ny = dt_moscow.astimezone(tz_ny)

print(dt_moscow, dt_moscow.tzinfo) # 2024-04-05 12:00:00+03:00
print(dt_ny, dt_ny.tzinfo) # 2024-04-05 05:00:00-04:00


Ключевой момент — никогда не делать так:

# ПЛОХО
dt_wrong = datetime(2024, 4, 5, 12, 0, 0, tzinfo=tz_moscow)


С pytz это ломает обработку переходов на летнее время. Нужно именно localize().

---

### Храним в UTC, показываем пользователю в его поясе

Золотое правило: хранить время в UTC, показывать — в локальном часовом поясе.

from datetime import datetime
import pytz

utc = pytz.utc
tz_user = pytz.timezone("Asia/Tokyo")

# допустим, это пришло из БД как UTC
dt_stored = datetime(2024, 4, 5, 9, 0, 0, tzinfo=utc)

dt_user = dt_stored.astimezone(tz_user)
print("UTC:", dt_stored)
print("User time:", dt_user)


Так вы избегаете боли при переносе данных между сервером, БД и пользователями из разных стран.

---

### Подводные камни: неоднозначные и несуществующие времена

При переходах на зимнее/летнее время могут быть:

- Неоднозначные моменты (1:30 случается дважды)
- Несуществующие моменты (стрелка перепрыгивает через 2:00–3:00)

pytz умеет это обрабатывать через параметр is_dst:

from datetime import datetime
import pytz

tz = pytz.timezone("America/New_York")
dt_naive = datetime(2024, 11, 3, 1, 30, 0) # переход на зимнее время

dt_first = tz.localize(dt_naive, is_dst=True) # "летняя" 1:30
dt_second = tz.localize(dt_naive, is_dst=False) # "зимняя" 1:30

print(dt_first)
print(dt_second)


---

### Вывод

- Используйте tzinfo, но руками его не реализуйте — для реального мира берите pytz.
- Делайте localize() для привязки наивного datetime к поясу.
- Всегда храните время в UTC, а отображайте в нужной зоне через astimezone().
- Будьте осторожны с переходами на летнее/зимнее время — они реально ломают голову, но pytz знает все правила.
👍2
Создание простого будильника с помощью модуля time
### Создаём простой будильник на Python с помощью модуля time

Когда только начинаешь учить Python, хочется писать что‑то полезное, а не только складывать числа в консоли. Давай сделаем простой будильник — без графики, без музыки, но с чёткой логикой и полезным знакомством с модулем time.

---

## Модуль time в двух словах

Главные функции, которые нам пригодятся:

- time.time() — возвращает текущее время в секундах с 1 января 1970 года.
- time.sleep(seconds) — “усыпляет” программу на заданное количество секунд.
- time.strftime(format) — возвращает текущие дату и время в удобном форматe.

---

## Будильник по времени в формате HH:MM

Идея простая:
1. Пользователь вводит время будильника.
2. Программа регулярно проверяет текущее время.
3. Когда часы и минуты совпадают — срабатывает будильник.

import time

def get_current_time_str():
return time.strftime("%H:%M")

alarm_time = input("Enter alarm time (HH:MM): ")

print(f"Alarm is set for {alarm_time}. Waiting...")

while True:
now = get_current_time_str()
if now == alarm_time:
print("WAKE UP! Alarm time reached!")
break
time.sleep(10)


Что здесь важно:

- time.strftime("%H:%M") возвращает строку вида 14:05.
- Мы сравниваем строку текущего времени с введённой строкой — просто и удобно.
- time.sleep(10) снижает нагрузку на процессор: мы проверяем время раз в 10 секунд, а не бесконечно крутим цикл.

---

## Будильник с задержкой в секундах

Иногда нужно “разбудить” себя (или программу) через N секунд:

import time

delay = int(input("Enter delay in seconds: "))
print(f"Timer set for {delay} seconds...")

time.sleep(delay)

print("TIME IS UP!")


Здесь мы используем только time.sleep(), но это уже рабочий таймер.

---

## Немного улучшений

- Можно сделать звуковой сигнал с помощью системных команд (winsound на Windows или os.system("play ...") на Linux), но это уже следующий шаг.
- Можно дать возможность вводить и дату, и время, затем сравнивать через time.strptime() и time.mktime() — получится почти мини‑календарь.

---

Мы использовали всего один модуль — time, а уже получили два рабочих инструмента: будильник по часам и таймер по секундам. Отличный пример того, как из простых функций собрать полезный скрипт.
👍4
Работа с мультипроцессорностью с помощью модуля multiprocessing