Создание таплов и однострочных функций с помощью
Python любит, когда код короткий и понятный. Сегодня разберём связку, которая идеально подходит для “быстрых” преобразований данных:
---
### 1. Что такое
Обычная функция:
То же самое с
А можно вообще не присваивать её переменной, а использовать сразу в выражении — и вот тут в игру вступает
---
### 2.
Типичный пример:
Обрати внимание:
---
### 3. Создаём таплы с помощью
Допустим, у нас есть список чисел, и мы хотим получить кортеж пар
Здесь важно:
-
-
-
---
### 4. Комбинируем несколько итерируемых объектов
Так можно элегантно “склеивать” данные без явных циклов.
---
### 5. Однострочные конвейеры преобразований
Связка
Считываем строки → сразу превращаем в числа → сразу умножаем.
---
### Когда это оправдано
- Нужно быстро преобразовать последовательность.
- Логика простая и умещается в одну строку.
- Нужен результат именно в виде кортежей (например, для неизменяемых данных или в качестве ключей словаря).
Если выражение становится громоздким и трудно читаемым — лучше вернуться к обычным функциям и циклам. Но для небольших преобразований
lambda и mapPython любит, когда код короткий и понятный. Сегодня разберём связку, которая идеально подходит для “быстрых” преобразований данных:
lambda + map + кортежи (tuples).---
### 1. Что такое
lambda и зачем она нужнаlambda — это способ создать функцию “на лету”, прямо внутри выражения, без def и имени.Обычная функция:
def square(x):
return x ** 2
То же самое с
lambda:square = lambda x: x ** 2
А можно вообще не присваивать её переменной, а использовать сразу в выражении — и вот тут в игру вступает
map.---
### 2.
map: применяем функцию ко всем элементамmap(func, iterable) берёт каждый элемент из iterable и прогоняет его через func. Типичный пример:
nums = [1, 2, 3, 4]
result = map(lambda x: x * 10, nums)
print(list(result)) # [10, 20, 30, 40]
Обрати внимание:
map возвращает итератор, поэтому часто его оборачивают в list() или tuple().---
### 3. Создаём таплы с помощью
map и lambdaДопустим, у нас есть список чисел, и мы хотим получить кортеж пар
(число, его квадрат):nums = [1, 2, 3, 4, 5]
pairs = tuple(
map(lambda x: (x, x ** 2), nums)
)
print(pairs)
# ((1, 1), (2, 4), (3, 9), (4, 16), (5, 25))
Здесь важно:
-
lambda x: (x, x ** 2) возвращает кортеж из двух элементов.-
map(...) создаёт поток таких кортежей.-
tuple(...) собирает их в один большой кортеж.---
### 4. Комбинируем несколько итерируемых объектов
map может работать сразу с несколькими последовательностями. Например, создадим кортеж таплов из координат:xs = [0, 1, 2]
ys = [10, 11, 12]
points = tuple(
map(lambda x, y: (x, y), xs, ys)
)
print(points)
# ((0, 10), (1, 11), (2, 12))
Так можно элегантно “склеивать” данные без явных циклов.
---
### 5. Однострочные конвейеры преобразований
Связка
map + lambda хорошо работает как мини-конвейер обработки данных:raw_data = ["1", "2", "3", "4"]
processed = tuple(
map(lambda x: int(x) * 2, raw_data)
)
print(processed)
# (2, 4, 6, 8)
Считываем строки → сразу превращаем в числа → сразу умножаем.
---
### Когда это оправдано
- Нужно быстро преобразовать последовательность.
- Логика простая и умещается в одну строку.
- Нужен результат именно в виде кортежей (например, для неизменяемых данных или в качестве ключей словаря).
Если выражение становится громоздким и трудно читаемым — лучше вернуться к обычным функциям и циклам. Но для небольших преобразований
lambda + map + tuple даёт лаконичный и выразительный код, который отлично вписывается в стиль Python.👍2🔥1
Объединение и фильтрация данных с
Представьте, что у вас есть конвейер обработки данных. Вход — «сырые» списки, выход — аккуратный результат. В Python роль такого конвейера идеально играют функции
---
###
То же самое через
---
###
Комбинация с
Сначала отфильтровали положительные, потом возводим их в квадрат.
---
###
На основе
---
### Все вместе: мини-аналитика
Допустим, у нас есть имена, оценки и флаг «сдал экзамен» (True/False). Нужно оставить только тех, кто сдал, и взять их имена и удвоенные баллы (например, за бонус).
---
filter, map, zipПредставьте, что у вас есть конвейер обработки данных. Вход — «сырые» списки, выход — аккуратный результат. В Python роль такого конвейера идеально играют функции
filter, map и zip. Разберём, как их сочетать так, чтобы код был короче, понятнее и «питонистее».---
###
filter: оставляем только нужноеfilter(func, iterable) пропускает через себя элементы, оставляя только те, для которых func возвращает True.numbers = [10, -3, 0, 25, -7, 8]
def is_positive(x):
return x > 0
positive_numbers = list(filter(is_positive, numbers))
print(positive_numbers) # [10, 25, 8]
То же самое через
lambda:positive_numbers = list(filter(lambda x: x > 0, numbers))
---
###
map: трансформируем элементыmap(func, iterable) применяет функцию к каждому элементу.numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # [1, 4, 9, 16]
Комбинация с
filter даёт уже мини-пайплайн:numbers = [-3, -1, 0, 1, 2, 3]
result = list(
map(
lambda x: x ** 2,
filter(lambda x: x > 0, numbers)
)
)
print(result) # [1, 4, 9]
Сначала отфильтровали положительные, потом возводим их в квадрат.
---
###
zip: объединяем несколько источников данныхzip склеивает элементы из нескольких итерируемых объектов по позициям.names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
paired = list(zip(names, scores))
print(paired) # [('Alice', 85), ('Bob', 92), ('Charlie', 78)]
На основе
zip удобно строить структуры данных:students = [
{"name": name, "score": score}
for name, score in zip(names, scores)
]
print(students)
# [{'name': 'Alice', 'score': 85}, ...]
---
### Все вместе: мини-аналитика
Допустим, у нас есть имена, оценки и флаг «сдал экзамен» (True/False). Нужно оставить только тех, кто сдал, и взять их имена и удвоенные баллы (например, за бонус).
names = ["Alice", "Bob", "Charlie", "Diana"]
scores = [85, 40, 73, 95]
passed = [True, False, True, True]
data = zip(names, scores, passed)
passed_transformed = list(
map(
lambda item: (item[0], item[1] * 2),
filter(lambda item: item[2], data)
)
)
print(passed_transformed)
# [('Alice', 170), ('Charlie', 146), ('Diana', 190)]
zip объединяет разрозненные списки в единый поток кортежей, filter выбрасывает тех, кто не сдал, map меняет формат результата и пересчитывает баллы.---
filter, map и zip — это кирпичики для построения аккуратных конвейеров обработки данных. В связке они позволяют писать код, который одновременно лаконичен и легко читается как последовательность шагов над данными.👍3
### Как использовать
Если вы когда‑нибудь считали что‑то с помощью словаря — количество слов, кликов, покупок, — вы наверняка писали что‑то вроде:
Работает, но выглядит громоздко. В Python есть инструмент, который делает это элегантнее —
---
## Что такое
Обычный словарь выбрасывает
Импорт:
Создание:
Теперь при первом обращении к
Никаких
---
## Пример 1: Подсчёт слов в тексте
---
## Пример 2: Группировка значений по ключу
Здесь
---
## Пример 3: Подсчёт сумм по категориям
---
## Когда
- Подсчёт частот (слов, кликов, событий).
- Группировка данных (пользователи по ролям, товары по категориям).
- Накопление сумм и списков без постоянных проверок
Формула запоминания простая:
“Если я пишу
defaultdict для подсчета значенийЕсли вы когда‑нибудь считали что‑то с помощью словаря — количество слов, кликов, покупок, — вы наверняка писали что‑то вроде:
counts = {}
for item in items:
if item in counts:
counts[item] += 1
else:
counts[item] = 1
Работает, но выглядит громоздко. В Python есть инструмент, который делает это элегантнее —
collections.defaultdict.---
## Что такое
defaultdictОбычный словарь выбрасывает
KeyError, если обратиться к несуществующему ключу. defaultdict вместо ошибки автоматически создаёт значение по умолчанию.Импорт:
from collections import defaultdict
Создание:
from collections import defaultdict
counts = defaultdict(int) # int() -> 0
Теперь при первом обращении к
counts[key] под капотом создаётся 0, и вы можете сразу увеличивать счётчик:for item in items:
counts[item] += 1
Никаких
if, всё уже есть.---
## Пример 1: Подсчёт слов в тексте
from collections import defaultdict
text = "banana apple banana orange apple banana"
words = text.split()
word_counts = defaultdict(int)
for word in words:
word_counts[word] += 1
print(dict(word_counts))
# {'banana': 3, 'apple': 2, 'orange': 1}
int как фабрика значений даёт 0 по умолчанию. Увеличиваем — и счётчик растёт.---
## Пример 2: Группировка значений по ключу
defaultdict полезен не только с числами. Частый сценарий — группировка.from collections import defaultdict
users = [
("alice", "admin"),
("bob", "user"),
("charlie", "admin"),
("david", "user"),
]
grouped = defaultdict(list)
for name, role in users:
grouped[role].append(name)
print(dict(grouped))
# {'admin': ['alice', 'charlie'], 'user': ['bob', 'david']}
Здесь
list() создаёт пустой список при первом доступе, и мы просто делаем .append().---
## Пример 3: Подсчёт сумм по категориям
from collections import defaultdict
sales = [
("books", 120),
("electronics", 300),
("books", 80),
("games", 150),
]
totals = defaultdict(float)
for category, amount in sales:
totals[category] += amount
print(dict(totals))
# {'books': 200.0, 'electronics': 300.0, 'games': 150.0}
float() даёт 0.0, удобно для денег и чисел с плавающей точкой.---
## Когда
defaultdict особенно полезен- Подсчёт частот (слов, кликов, событий).
- Группировка данных (пользователи по ролям, товары по категориям).
- Накопление сумм и списков без постоянных проверок
if key in dict.Формула запоминания простая:
“Если я пишу
if key in d: ... else: ... — возможно, мне нужен defaultdict.”👍2
Отладка Python-кода с помощью встроенного модуля
Начинающие часто боятся отладки как чего-то «магического». На самом деле
---
### Зачем нужен
- останавливать программу в нужной точке;
- смотреть значения переменных;
- выполнять произвольные выражения;
- шагать по коду, строка за строкой.
---
### Самый простой старт:
С Python 3.7 появился встроенный вызов:
Запускаем скрипт в терминале как обычно:
В месте
---
### Базовые команды
Находясь в
-
-
-
-
-
-
Например, в нашем примере:
Вы сразу видите реальную причину будущей ошибки, не дожидаясь падения программы.
---
### Ручной запуск:
Иногда удобно запустить отладчик с самого старта:
Вы окажетесь в
Команда
---
### Отладка циклов и логики
Пример с циклом, где что-то идёт не так:
Когда цикл дойдёт до
Так легко понять, что отрицательные числа вообще не должны участвовать в сумме — и исправить логику.
---
### Несколько практических подсказок
- Не оставляйте
- Научитесь минимуму команд:
-
pdbНачинающие часто боятся отладки как чего-то «магического». На самом деле
pdb — это всего лишь интерактивная пауза в вашем коде, где вы можете посмотреть, что происходит «под капотом», шаг за шагом.---
### Зачем нужен
pdb?print-отладка быстро превращает код в кашу. pdb позволяет:- останавливать программу в нужной точке;
- смотреть значения переменных;
- выполнять произвольные выражения;
- шагать по коду, строка за строкой.
---
### Самый простой старт:
breakpoint()С Python 3.7 появился встроенный вызов:
def divide(a, b):
result = a / b
return result
def main():
x = 10
y = 0
breakpoint() # здесь выполнение остановится
print(divide(x, y))
if __name__ == "__main__":
main()
Запускаем скрипт в терминале как обычно:
python script.py
В месте
breakpoint() вы попадете в интерактивную консоль pdb с приглашением вида (Pdb).---
### Базовые команды
pdbНаходясь в
(Pdb), попробуйте:-
l — показать фрагмент исходника (list).-
n — выполнить следующую строку (next).-
s — шагнуть внутрь функции (step).-
c — продолжить выполнение до следующей точки останова (continue).-
p x — вывести значение переменной x (print).-
q — выйти из отладки (quit).Например, в нашем примере:
(Pdb) p x
10
(Pdb) p y
0
(Pdb) p divide(x, y)
ZeroDivisionError: division by zero
Вы сразу видите реальную причину будущей ошибки, не дожидаясь падения программы.
---
### Ручной запуск:
python -m pdbИногда удобно запустить отладчик с самого старта:
python -m pdb script.py
Вы окажетесь в
pdb ещё до выполнения первой строки. Тут можно заранее поставить точки останова:(Pdb) b main
(Pdb) c
Команда
b main ставит брейкпоинт на вход в функцию main.---
### Отладка циклов и логики
Пример с циклом, где что-то идёт не так:
def sum_positive(numbers):
total = 0
for n in numbers:
if n < 0:
breakpoint()
total += n
return total
print(sum_positive([1, 2, -5, 3]))
Когда цикл дойдёт до
-5, pdb остановится. Можно исследовать:(Pdb) p n
-5
(Pdb) p total
3
(Pdb) p numbers
[1, 2, -5, 3]
Так легко понять, что отрицательные числа вообще не должны участвовать в сумме — и исправить логику.
---
### Несколько практических подсказок
- Не оставляйте
breakpoint() в продакшене: вы можете обернуть его в условие по переменной окружения.- Научитесь минимуму команд:
l, n, s, c, p, q — этого достаточно, чтобы комфортно отлаживать большинство багов.-
pdb работает везде, где есть консоль: локально, в Docker-контейнере, на сервере.pdb — лучший способ превратить «странные баги» в понятные и воспроизводимые ситуации. Чем раньше вы привыкнете к нему, тем быстрее перестанете бояться ошибок и начнёте управлять ими.👍3❤1
Работа с локализацией и форматами: модуль
Если ваш скрипт должен показывать даты, деньги и числа «по‑местному», голый
---
### Базовая настройка локали
---
### Форматирование чисел
Параметр
Одна и та же величина — разное отображение.
---
### Валюта по‑местному
---
### Парсинг строк в числа
Иногда нужно не только выводить, но и «понимать» локальные числа:
---
### Полезные выводы
-
- Локаль влияет на весь процесс в пределах программы, так что в больших проектах её обычно настраивают централизованно.
- Для кроссплатформенности проверяйте, что нужная локаль есть в системе, и держите запасной вариант (например,
Модуль
localeЕсли ваш скрипт должен показывать даты, деньги и числа «по‑местному», голый
print() быстро перестаёт хватать. В России — запятая в дробях и пробелы между разрядами, в США — точка и запятая, в Германии — наоборот. Всё это умеет модуль locale.---
### Базовая настройка локали
import locale
# Устанавливаем локаль по умолчанию системы
locale.setlocale(locale.LC_ALL, '')
current_locale = locale.getlocale()
print("Current locale:", current_locale)
LC_ALL — сразу все категории (числа, даты, деньги и т.д.). Можно настраивать точечно: LC_NUMERIC, LC_TIME, LC_MONETARY и др.---
### Форматирование чисел
import locale
locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8') # может отличаться в вашей системе
value = 1234567.89
formatted = locale.format_string('%.2f', value, grouping=True)
print(formatted) # например: 1 234 567,89
Параметр
grouping=True включает разделители разрядов. Если сменить локаль:locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
print(locale.format_string('%.2f', value, grouping=True)) # 1,234,567.89
Одна и та же величина — разное отображение.
---
### Валюта по‑местному
import locale
locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8')
price = 1999.99
formatted_price = locale.currency(price, grouping=True)
print(formatted_price) # например: 1.999,99 €
locale.currency() учитывает символ валюты, позицию знака, пробелы и разделители разрядов.---
### Парсинг строк в числа
Иногда нужно не только выводить, но и «понимать» локальные числа:
import locale
locale.setlocale(locale.LC_ALL, 'fr_FR.UTF-8')
s = "1 234,56"
x = locale.atof(s) # string -> float с учётом локали
print(x) # 1234.56 (обычный Python float)
atof() и atoi() разбирают строки по локальным правилам — важно при вводе данных от пользователя из разных стран.---
### Полезные выводы
-
locale не меняет сами числа, он управляет представлением (форматом).- Локаль влияет на весь процесс в пределах программы, так что в больших проектах её обычно настраивают централизованно.
- Для кроссплатформенности проверяйте, что нужная локаль есть в системе, и держите запасной вариант (например,
C или en_US.UTF-8).Модуль
locale — это способ сделать ваш код дружелюбным к пользователю вне зависимости от того, где он живёт и какой у него разделитель дробной части.👍3
Основы проверки кода с использованием pylint и flake8
Когда начинаешь писать на Python, код обычно «работает — и ладно». Но довольно быстро становится ясно: читать его через неделю невозможно даже самому автору. Тут на сцену выходят линтеры — инструменты автоматической проверки стиля и потенциальных ошибок. Два самых популярных:
---
### Зачем вообще нужны линтеры?
Линтеры помогают:
- находить опечатки и потенциальные баги (неиспользуемые переменные, лишние импорты);
- поддерживать единый стиль кода (отступы, длина строк, имена переменных);
- учиться писать «питоничный» код — по PEP 8 и здравому смыслу.
---
### Установка
Проверка файла:
---
### Простой пример «плохого» кода
Типичные проблемы, которые найдут линтеры:
- несколько импортов в одной строке (
- несогласованные отступы;
- отсутствие пробелов вокруг операторов;
- вызов
---
### Как это исправить
Теперь
---
### В чём разница между pylint и flake8?
- flake8 — лёгкий и быстрый. Фокусируется на стиле и базовых ошибках.
- pylint — гораздо более строгий: анализирует структуру проекта, связи между модулями, даёт «оценку» кода, умеет ловить более сложные проблемы.
Часто их используют вместе:
---
### Настройка под себя
Оба инструмента можно «приручить» конфигами:
-
-
Например, увеличить допустимую длину строки:
---
Лучший момент начать использовать линтеры — сейчас. Они быстро превращают хаотичный учебный код в аккуратный и читаемый, а заодно приучают к хорошему стилю, почти как строгий, но полезный наставник.
Когда начинаешь писать на Python, код обычно «работает — и ладно». Но довольно быстро становится ясно: читать его через неделю невозможно даже самому автору. Тут на сцену выходят линтеры — инструменты автоматической проверки стиля и потенциальных ошибок. Два самых популярных:
pylint и flake8.---
### Зачем вообще нужны линтеры?
Линтеры помогают:
- находить опечатки и потенциальные баги (неиспользуемые переменные, лишние импорты);
- поддерживать единый стиль кода (отступы, длина строк, имена переменных);
- учиться писать «питоничный» код — по PEP 8 и здравому смыслу.
---
### Установка
pip install pylint flake8
Проверка файла:
pylint my_script.py
flake8 my_script.py
---
### Простой пример «плохого» кода
# file: bad_example.py
import os, sys
def add(a,b):
return a+ b
def main():
result = add(2,2)
print("Result:",result)
main()
Типичные проблемы, которые найдут линтеры:
- несколько импортов в одной строке (
import os, sys);- несогласованные отступы;
- отсутствие пробелов вокруг операторов;
- вызов
main() без проверки if __name__ == "__main__":.---
### Как это исправить
# file: good_example.py
import os
import sys
def add(a: int, b: int) -> int:
return a + b
def main() -> None:
result = add(2, 2)
print("Result:", result)
if __name__ == "__main__":
main()
Теперь
flake8 в основном будет молчать, а pylint может ещё подсказать, что os и sys нигде не используются.---
### В чём разница между pylint и flake8?
- flake8 — лёгкий и быстрый. Фокусируется на стиле и базовых ошибках.
- pylint — гораздо более строгий: анализирует структуру проекта, связи между модулями, даёт «оценку» кода, умеет ловить более сложные проблемы.
Часто их используют вместе:
flake8 — для стилистики, pylint — для более глубокой проверки.---
### Настройка под себя
Оба инструмента можно «приручить» конфигами:
-
pylint: файл .pylintrc-
flake8: файл .flake8 или раздел [flake8] в setup.cfgНапример, увеличить допустимую длину строки:
# .flake8
[flake8]
max-line-length = 100
---
Лучший момент начать использовать линтеры — сейчас. Они быстро превращают хаотичный учебный код в аккуратный и читаемый, а заодно приучают к хорошему стилю, почти как строгий, но полезный наставник.
👍2
Как организовать модульную структуру проекта в Python
Когда скрипт на 50 строк превращается в файл на 500, наступает момент, когда хочется просто… закрыть редактор. Тут и приходит на помощь модульная структура: разбиваем хаос на аккуратные части, которые легко читать, тестировать и переиспользовать.
---
### Что такое модуль и пакет?
- Модуль — обычный
- Пакет — папка с Python-модулями. Обычно содержит файл
Простейшая структура проекта может выглядеть так:
Но уже на среднем проекте удобнее перейти к пакетам:
---
### Пример: мини-приложение с модулями
Представим, что пишем упрощенную систему заказов.
Мы разделили:
- models — только данные.
- services — бизнес-логика.
- utils — вспомогательные функции.
Каждый файл отвечает за свою зону, и код перестает быть «простыней».
---
### Относительные и абсолютные импорты
Внутри пакета удобно использовать относительные импорты:
А снаружи — абсолютные:
Это делает структуру понятной: глядя на импорт, сразу видно, откуда что приходит.
---
### Зачем все это?
- Проще тестировать: можно импортировать один модуль и тестировать его изолированно.
- Легче находить код: логика не размазана по одному файлу.
- Удобно расширять проект: добавили новый модуль — остальное не пострадало.
Модульная структура — это не «красиво», это «жить можно», когда код растет. Начать лучше сразу, даже если проект пока умещается в один файл.
Когда скрипт на 50 строк превращается в файл на 500, наступает момент, когда хочется просто… закрыть редактор. Тут и приходит на помощь модульная структура: разбиваем хаос на аккуратные части, которые легко читать, тестировать и переиспользовать.
---
### Что такое модуль и пакет?
- Модуль — обычный
.py-файл. Например: utils.py.- Пакет — папка с Python-модулями. Обычно содержит файл
__init__.py (в современных версиях Python он не обязателен, но лучше его иметь для явности).Простейшая структура проекта может выглядеть так:
my_project/
main.py
models.py
utils.py
Но уже на среднем проекте удобнее перейти к пакетам:
my_project/
main.py
app/
__init__.py
models.py
services.py
utils.py
---
### Пример: мини-приложение с модулями
Представим, что пишем упрощенную систему заказов.
app/models.py:from dataclasses import dataclass
@dataclass
class Order:
id: int
amount: float
is_paid: bool = False
app/services.py:from .models import Order
from .utils import apply_discount
def process_order(order: Order, discount: float) -> Order:
order.amount = apply_discount(order.amount, discount)
order.is_paid = True
return order
app/utils.py:def apply_discount(amount: float, discount: float) -> float:
if not 0 <= discount <= 1:
raise ValueError("Invalid discount")
return round(amount * (1 - discount), 2)
main.py:from app.models import Order
from app.services import process_order
def main():
order = Order(id=1, amount=100.0)
order = process_order(order, discount=0.15)
print(order)
if __name__ == "__main__":
main()
Мы разделили:
- models — только данные.
- services — бизнес-логика.
- utils — вспомогательные функции.
Каждый файл отвечает за свою зону, и код перестает быть «простыней».
---
### Относительные и абсолютные импорты
Внутри пакета удобно использовать относительные импорты:
from .models import Order
from .utils import apply_discount
А снаружи — абсолютные:
from app.services import process_order
Это делает структуру понятной: глядя на импорт, сразу видно, откуда что приходит.
---
### Зачем все это?
- Проще тестировать: можно импортировать один модуль и тестировать его изолированно.
- Легче находить код: логика не размазана по одному файлу.
- Удобно расширять проект: добавили новый модуль — остальное не пострадало.
Модульная структура — это не «красиво», это «жить можно», когда код растет. Начать лучше сразу, даже если проект пока умещается в один файл.
🔥2👍1
Создание собственных исключений для понятной обработки ошибок
Стандартные исключения Python (
---
## Зачем нужны свои исключения
Представьте интернет‑магазин. Ошибка “ValueError” может означать всё что угодно: от некорректной цены до пустого списка товаров. Гораздо понятнее:
-
-
-
Код сразу становится самодокументируемым: по имени исключения видно, что пошло не так и где это обрабатывать.
---
## Как создать своё исключение
Создание простое: наследуемся от
Используем:
---
## Иерархия исключений
Красота начинается, когда вы строите семейство ошибок:
Теперь можно:
Преимущество: вы можете ловить как конкретные, так и все “магазинные” ошибки разом.
---
## Добавляем контекст к исключениям
Исключения могут не только “орать”, но и нести данные:
Использование:
Так обработчик ошибок видит не только текст, но и структурированные данные.
---
## Когда точно стоит заводить своё исключение
- Ошибка связана с предметной областью (деньги, заказы, уровни в игре).
- Вам нужно по‑разному реагировать на разные типы ошибок.
- Вы пишете код, которым будут пользоваться другие разработчики.
Если же ошибка — просто “не тот тип” или “не то значение”, часто достаточно стандартных исключений.
Собственные исключения — это не “косметика”, а мощный инструмент архитектуры. Они превращают хаос “что-то упало” в чёткую систему сигналов, по которой программа может разумно реагировать на проблемы.
Стандартные исключения Python (
ValueError, TypeError, KeyError и т.д.) хороши, но иногда они слишком общие. Если вы пишете библиотеку, игру или веб‑сервис, вам нужно чётко понимать: какая именно логическая ошибка произошла и что с ней делать. Тут и вступают в игру собственные исключения.---
## Зачем нужны свои исключения
Представьте интернет‑магазин. Ошибка “ValueError” может означать всё что угодно: от некорректной цены до пустого списка товаров. Гораздо понятнее:
-
InvalidPriceError-
OutOfStockError-
PaymentDeclinedErrorКод сразу становится самодокументируемым: по имени исключения видно, что пошло не так и где это обрабатывать.
---
## Как создать своё исключение
Создание простое: наследуемся от
Exception (или другого базового класса исключений):class InvalidPriceError(Exception):
"""Raised when product price is invalid."""
pass
Используем:
def set_price(price: float) -> None:
if price <= 0:
raise InvalidPriceError(f"Price must be positive, got: {price}")
---
## Иерархия исключений
Красота начинается, когда вы строите семейство ошибок:
class ShopError(Exception):
"""Base exception for shop-related errors."""
pass
class OutOfStockError(ShopError):
pass
class PaymentError(ShopError):
pass
class PaymentDeclinedError(PaymentError):
pass
Теперь можно:
try:
process_order(order)
except PaymentDeclinedError as e:
log_warning(e)
show_message("Payment was declined, try another card.")
except ShopError as e:
log_error(e)
show_message("Something went wrong with your order.")
Преимущество: вы можете ловить как конкретные, так и все “магазинные” ошибки разом.
---
## Добавляем контекст к исключениям
Исключения могут не только “орать”, но и нести данные:
class OutOfStockError(ShopError):
def __init__(self, product_id: int, requested: int, available: int):
self.product_id = product_id
self.requested = requested
self.available = available
message = (f"Product {product_id}: requested {requested}, "
f"available {available}")
super().__init__(message)
Использование:
try:
reserve_product(product_id=42, quantity=10)
except OutOfStockError as e:
print(e.product_id, e.requested, e.available)
Так обработчик ошибок видит не только текст, но и структурированные данные.
---
## Когда точно стоит заводить своё исключение
- Ошибка связана с предметной областью (деньги, заказы, уровни в игре).
- Вам нужно по‑разному реагировать на разные типы ошибок.
- Вы пишете код, которым будут пользоваться другие разработчики.
Если же ошибка — просто “не тот тип” или “не то значение”, часто достаточно стандартных исключений.
Собственные исключения — это не “косметика”, а мощный инструмент архитектуры. Они превращают хаос “что-то упало” в чёткую систему сигналов, по которой программа может разумно реагировать на проблемы.
🔥3❤2👍1
Простое шифрование текста с базовым алгоритмом Caesar
Когда-то, задолго до хакеров и брутфорса, шифрование могло выглядеть почти игрушкой. Один из самых простых и известных методов — шифр Цезаря. Он настолько прост, что идеально подходит для первых шагов в программировании и работе со строками в Python.
Идея простая: каждую букву заменяем другой, сдвинутой в алфавите на фиксированное число позиций. Например, при сдвиге 3 буква
### Базовая реализация
Сделаем функцию, которая шифрует только латинские буквы, сохраняя регистр и игнорируя символы вне алфавита:
Функция
### Расшифровка — тот же алгоритм
Дешифровать можно тем же кодом, просто меняя знак сдвига:
Мы не пишем второй алгоритм, а переиспользуем первый — хороший пример того, как небольшая продуманная функция упрощает жизнь.
### Немного автоматизации
Добавим простую «оболочку», чтобы пользователь вводил текст и сдвиг:
Такой простой проект полезен сразу в нескольких вещах:
- работа со строками и символами;
- понимание циклов и условий;
- практика написания маленьких, переиспользуемых функций.
Шифр Цезаря давно не считается надежным, но как учебный пример — это отличный старт, чтобы почувствовать, как из простых операций над символами рождается алгоритм шифрования.
Когда-то, задолго до хакеров и брутфорса, шифрование могло выглядеть почти игрушкой. Один из самых простых и известных методов — шифр Цезаря. Он настолько прост, что идеально подходит для первых шагов в программировании и работе со строками в Python.
Идея простая: каждую букву заменяем другой, сдвинутой в алфавите на фиксированное число позиций. Например, при сдвиге 3 буква
A превращается в D, B в E и так далее по кругу.### Базовая реализация
Сделаем функцию, которая шифрует только латинские буквы, сохраняя регистр и игнорируя символы вне алфавита:
def caesar_encrypt(text, shift):
result = []
for ch in text:
if 'a' <= ch <= 'z':
base = ord('a')
offset = (ord(ch) - base + shift) % 26
result.append(chr(base + offset))
elif 'A' <= ch <= 'Z':
base = ord('A')
offset = (ord(ch) - base + shift) % 26
result.append(chr(base + offset))
else:
result.append(ch)
return ''.join(result)
message = "Hello, World!"
encrypted = caesar_encrypt(message, 3)
print(encrypted) # Khoor, Zruog!
Функция
ord превращает символ в его числовой код, chr делает обратное, а % 26 обеспечивает «цикличность» по алфавиту.### Расшифровка — тот же алгоритм
Дешифровать можно тем же кодом, просто меняя знак сдвига:
def caesar_decrypt(text, shift):
return caesar_encrypt(text, -shift)
decrypted = caesar_decrypt(encrypted, 3)
print(decrypted) # Hello, World!
Мы не пишем второй алгоритм, а переиспользуем первый — хороший пример того, как небольшая продуманная функция упрощает жизнь.
### Немного автоматизации
Добавим простую «оболочку», чтобы пользователь вводил текст и сдвиг:
def run_caesar():
text = input("Enter text: ")
shift = int(input("Enter shift (e.g. 3): "))
mode = input("Mode (e for encrypt, d for decrypt): ").strip().lower()
if mode == 'e':
print("Encrypted:", caesar_encrypt(text, shift))
elif mode == 'd':
print("Decrypted:", caesar_decrypt(text, shift))
else:
print("Unknown mode")
if __name__ == "__main__":
run_caesar()
Такой простой проект полезен сразу в нескольких вещах:
- работа со строками и символами;
- понимание циклов и условий;
- практика написания маленьких, переиспользуемых функций.
Шифр Цезаря давно не считается надежным, но как учебный пример — это отличный старт, чтобы почувствовать, как из простых операций над символами рождается алгоритм шифрования.
👍2
Создание календаря событий с помощью модуля
Модуль
---
### Базовый календарь на месяц
Начнем с простого вывода календаря:
Каждая строка — неделя, нули означают «нет дня» (пустая клетка в начале/конце месяца).
---
### Добавляем события
Сделаем простейший календарь событий: у нас есть словарь
Результат — текстовый календарь, где дни с событиями помечены
---
### Поиск событий по дате и будням
Так можно, например, подсветить события, которые попадают на выходные, или не планировать релизы на понедельник.
---
calendarМодуль
calendar — один из тех, что часто игнорируют, а зря. С его помощью можно быстро сделать простой «календарь событий» прямо в консоли или подготовить данные для бота/веб-приложения.---
### Базовый календарь на месяц
Начнем с простого вывода календаря:
import calendar
year = 2025
month = 1
cal = calendar.TextCalendar(firstweekday=0) # 0 — понедельник
print(cal.formatmonth(year, month))
TextCalendar рисует аккуратную таблицу в виде текста. Если нужен «машиночитаемый» вариант, лучше использовать monthcalendar:import calendar
year = 2025
month = 1
weeks = calendar.monthcalendar(year, month)
for week in weeks:
print(week)
Каждая строка — неделя, нули означают «нет дня» (пустая клетка в начале/конце месяца).
---
### Добавляем события
Сделаем простейший календарь событий: у нас есть словарь
events, где ключ — кортеж (year, month, day), а значение — текст события. Отметим события звездочкой:import calendar
events = {
(2025, 1, 5): "Project deadline",
(2025, 1, 12): "Team meeting",
(2025, 1, 31): "Release day",
}
def print_events_calendar(year, month):
cal = calendar.TextCalendar(firstweekday=0)
weeks = calendar.monthcalendar(year, month)
print(f"{calendar.month_name[month]} {year}".center(20))
print("Mo Tu We Th Fr Sa Su")
for week in weeks:
row = []
for day in week:
if day == 0:
row.append(" ")
continue
mark = "*" if (year, month, day) in events else " "
cell = f"{day:2d}{mark}"
# обрежем до 3 символов, чтобы выровнять сетку
row.append(cell[:3])
print(" ".join(row))
print_events_calendar(2025, 1)
Результат — текстовый календарь, где дни с событиями помечены
*. Можно менять символ на !, # и т.п.---
### Поиск событий по дате и будням
calendar знает всё о днях недели:import calendar
from datetime import date
def is_weekend(year, month, day):
weekday = date(year, month, day).weekday() # 0=Mon, 6=Sun
return weekday >= 5
for key, title in events.items():
y, m, d = key
day_name = calendar.day_name[date(y, m, d).weekday()]
tag = " (weekend)" if is_weekend(y, m, d) else ""
print(f"{y}-{m:02d}-{d:02d}: {title} — {day_name}{tag}")
Так можно, например, подсветить события, которые попадают на выходные, или не планировать релизы на понедельник.
---
calendar отлично подходит для прототипирования: быстрый текстовый календарь, базовая логика дат, дни недели, учет високосных лет — все уже есть «из коробки». Остается только обернуть это в интерфейс, который вам нужен.👍3
### Работа с временными зонами и UTC: почему
Работа со временем — один из самых подлых участков в Python-проектах. Летнее время, разные часовые пояса, сервер в UTC, пользователь в другом конце планеты — всё это легко ломает логику. Разберём, как навести порядок с помощью модуля
---
## Наивные и «осознанные» даты
Модуль
- Наивный
- Aware (
Такой объект опасен: сравнения и арифметика могут работать неверно, если смешивать его с датами из других зон.
---
## Добавляем pytz и приводим всё к UTC
Золотое правило: внутри приложения хранить время в UTC, а локальное время использовать только на входе/выходе.
Теперь
---
## Локальное время пользователя → UTC
Допустим, пользователь в Нью-Йорке выбирает время встречи:
Важно: нельзя делать
---
## UTC → локальное время пользователя
Когда храним в базе UTC, а показываем пользователю в его поясе:
---
## Типичные правила, чтобы не утонуть во времени
1. Всегда сохраняй UTC в базе.
2. На границе системы:
- входящие данные → локализуешь через
- исходящие данные → из UTC в нужную зону через
3. Не используй
4. Явно задавай
pytz до сих пор нуженРабота со временем — один из самых подлых участков в Python-проектах. Летнее время, разные часовые пояса, сервер в UTC, пользователь в другом конце планеты — всё это легко ломает логику. Разберём, как навести порядок с помощью модуля
pytz.---
## Наивные и «осознанные» даты
Модуль
datetime в Python умеет хранить даты с информацией о временной зоне и без неё.- Наивный
datetime не знает, в каком он часовом поясе.- Aware (
tz-aware) — содержит таймзону и может корректно переводиться в другие.from datetime import datetime
naive_dt = datetime.now()
print(naive_dt.tzinfo) # None — часовой пояс неизвестен
Такой объект опасен: сравнения и арифметика могут работать неверно, если смешивать его с датами из других зон.
---
## Добавляем pytz и приводим всё к UTC
Золотое правило: внутри приложения хранить время в UTC, а локальное время использовать только на входе/выходе.
from datetime import datetime
import pytz
utc = pytz.utc
utc_now = datetime.now(utc)
print(utc_now, utc_now.tzinfo) # Время в UTC
Теперь
utc_now — «осознанный» объект, и его можно безопасно конвертировать в любой другой часовой пояс.---
## Локальное время пользователя → UTC
Допустим, пользователь в Нью-Йорке выбирает время встречи:
from datetime import datetime
import pytz
user_tz = pytz.timezone("America/New_York")
# Пользователь ввёл время (например, из формы)
naive_meeting = datetime(2025, 3, 10, 15, 0, 0)
# Правильный способ "прикрутить" таймзону
local_meeting = user_tz.localize(naive_meeting)
utc_meeting = local_meeting.astimezone(pytz.utc)
print("Local:", local_meeting)
print("UTC: ", utc_meeting)
Важно: нельзя делать
naive_meeting.replace(tzinfo=user_tz) — это обходит правила перехода на летнее/зимнее время и даёт неверный результат.---
## UTC → локальное время пользователя
Когда храним в базе UTC, а показываем пользователю в его поясе:
import pytz
# Время из БД в UTC
stored_utc = utc_meeting # из предыдущего примера
user_tz = pytz.timezone("Asia/Tokyo")
user_time = stored_utc.astimezone(user_tz)
print("For user in Tokyo:", user_time)
pytz учитывает исторические изменения, летнее время и прочие «сюрпризы» календарей.---
## Типичные правила, чтобы не утонуть во времени
1. Всегда сохраняй UTC в базе.
2. На границе системы:
- входящие данные → локализуешь через
timezone.localize(...) → переводишь в UTC;- исходящие данные → из UTC в нужную зону через
astimezone(...).3. Не используй
replace(tzinfo=...) для смены зоны.4. Явно задавай
tzinfo, не полагайся на наивные datetime.now().pytz выглядит простым, но помогает избежать очень дорогих ошибок во времени, особенно когда пользователи живут в разных точках мира.👍2