PytStart | Программирование на Python
4.45K subscribers
20 photos
26 videos
54 links
Python: примеры кода, уроки, статьи

Купить рекламу: https://telega.in/c/pytstart

✍️По всем вопросам: @Pascal4eg
Download Telegram
🧩 Structural Pattern Matching в Python 3.10+ — наконец-то нормальный match-case

Ты ждал switch в Python?
Python 3.10 привёз не просто switch, а настоящий мощный match — с распаковкой, шаблонами, guard’ами и даже классами.

👍 Базовое использование match-case

def handle_command(cmd):
match cmd:
case "start":
print("Запуск...")
case "stop":
print("Остановка.")
case _:
print("Неизвестная команда.")

_ — это как default в switch. Срабатывает, если ничего не подошло.

👍 Распаковка списков

def handle_coords(point):
match point:
case [0, 0]:
print("Начало координат")
case [x, 0]:
print(f"На оси X: x = {x}")
case [0, y]:
print(f"На оси Y: y = {y}")
case [x, y]:
print(f"Где-то в пространстве: ({x}, {y})")

Можно писать как шаблоны — работает магия распаковки прямо в кейсах.

👍 Распаковка словарей

def describe_user(user):
match user:
case {"name": name, "age": age}:
print(f"{name}, возраст {age}")
case {"name": name}:
print(f"Имя: {name}, возраст неизвестен")

Словари матчатся по ключам.


👍 match-классы с match_args

class Point:
__match_args__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y

def process(obj):
match obj:
case Point(0, 0):
print("Центр")
case Point(x, y):
print(f"Точка: ({x}, {y})")

Так можно матчить прямо по объектам.

👍 Guard — фильтрация в кейсе

def check_number(n):
match n:
case x if x > 0:
print("Положительное")
case x if x < 0:
print("Отрицательное")
case _:
print("Ноль")

Можно фильтровать значения прямо в кейсе.

👍 Матч с распаковкой и остатком

def analyze(data):
match data:
case [first, *middle, last]:
print(f"Начало: {first}, конец: {last}, середина: {middle}")

*middle — как в списках: «всё, что посередине».

👍 Комбинации: tuple + шаблон + guard

def react(event):
match event:
case ("click", x, y) if x > 0 and y > 0:
print("Клик по области")
case ("keypress", key):
print(f"Нажата клавиша {key}")
case _:
print("Неизвестное событие")

Можно матчить по типу события, распаковывать и фильтровать — мощь.

🗣️ Запомни: Structural Pattern Matching — это не просто switch, это способ писать выразительный, компактный и читабельный код.
Please open Telegram to view this post
VIEW IN TELEGRAM
3
🐍 Clite ультра‑лёгкий zero‑dependency фреймворк для CLI на Python

Хочешь писать свои CLI‑утилиты без кучи зависимостей? Новый проект Clite от искусного автора на Хабре — как раз то, что нужно. Это minimal‑вес без Typer, Click и прочего — всего \~300 строк кода, и при этом поддержка type hints и декораторов знакомых из FastAPI/Flask!

🚀 В статье ты узнаешь:

📌 Почему автор выбрал минимализм и вместо Click/Typer реализовал свой парсер аргументов
📌 Как реализована поддержка @app.command() и аннотации для аргументов
📌 Почему зависимостей почти нет (только typing-extensions)
📌 Взгляд в roadmap — sub‑commands, Annotated, свой echo и др.
📌 Как сделать свой CLI‑инструмент небольшим, понятным и удобным

➡️ Читайте и наслаждайтесь

🗣️ Если тебе близок minimalist‑стиль, хочешь учиться у автора и писать свои инструменты — Clite просто must‑read!

🤩 Pytstart ||#Статья #Python #CLI #ZeroDeps
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍2
🎨 Редкие и красивые Python‑библиотеки для творчества

Хочешь выйти за рамки Pandas и Flask? В этой статье — необычные библиотеки, которые позволяют рисовать, генерировать графику и создавать интерактивную визуализацию красивого уровня.

В статье ты узнаешь:

📌 TurtlePlus и geometrik — рекурсивные рисунки, фракталы, тесселяции
📌 HersheyFonts — винтажные векторные шрифты для графики и ASCII-арта
📌 drawSvg — создание слоистых SVG‑иллюстраций и анимаций
📌 PyCirclize — кольцевые диаграммы, иерархические визуализации, идеальны для научных данных
📌 Почему это не просто art‑проекты — а способы думать визуально и программно

➡️ Читайте и наслаждайтесь

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

🤩 Pytstart || ##Creative #Статья
Please open Telegram to view this post
VIEW IN TELEGRAM
👍52🔥2
🪄 __slots__ в Python — ускоряем и экономим на классах

Если ты создаёшь кучу объектов, каждый по умолчанию несёт с собой `__dict__`, который ест память.
А теперь представь: у тебя 100 000 таких объектов. Зачем лишний мусор?

🔧 Без __slots__: обычный класс
class User:
def __init__(self, name, email):
self.name = name
self.email = email

users = [User(f"user{i}", f"u{i}@mail.com") for i in range(100_000)]

Каждый объект несёт __dict__. Замерим:
import sys
u = User("test", "t@mail.com")
print(sys.getsizeof(u)) # 56
print(sys.getsizeof(u.__dict__)) # 232

📦 Итог: \~300 байт на одного → \~30 МБ на 100К объектов.

⚡️ С __slots__
class User:
__slots__ = ['name', 'email']

def __init__(self, name, email):
self.name = name
self.email = email

users = [User(f"user{i}", f"u{i}@mail.com") for i in range(100_000)]


u = User("test", "t@mail.com")
print(sys.getsizeof(u)) # 48


💥 Почти в 5 раз меньше памяти. И никакого __dict__:
print(hasattr(u, '__dict__'))  # False


🚫 Слоты = строгая структура
u.age = 42
# AttributeError: 'User' object has no attribute 'age'



🗣️ Запомни:__slots__ — это жёстко, экономно и быстро.Ты получаешь фиксированную структуру, меньше памяти и ускорение доступа к атрибутам.
Не юзай их везде — только там, где реально важно.
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍2
🖥 Как работает хэш-таблица в Python (на примере dict)

Хэш-таблица это разреженный массив (массив, в котором имеются незаполненные позиции). В стандартных англоязычных учебниках ячейки хэш-таблицы называются "bucket". В хэш-таблице dict каждому элементу соотвествует ячейка, содержащая два поля: ссылку на ключ и ссылку на значение элемента. Поскольку размер всех ячеек одинаков, доступ к отдельной ячейке производится по смещению.

Хэширование ключа
Когда вы добавляете элемент в словарь, Python вычисляет хэш-значение ключа с помощью встроенной функции hash(). Это хэш-значение определяет, в какую "ячейку" таблицы (индекс в массиве) поместить пару ключ: значение.

Быстрый доступ
При обращении по ключу, Python снова вычисляет хэш ключа и сразу "прыгает" к нужной ячейке — доступ происходит в среднем за O(1).

Коллизии
Если два разных ключа имеют одинаковый хэш (редко, но возможно), Python использует открытую адресацию — ищет ближайшую свободную ячейку.

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

Рехэширование
Когда словарь сильно заполняется, Python автоматически увеличивает размер таблицы и перераспределяет элементы (это дорогостоящая операция, но происходит не часто).


📌 Ключевые особенности:

Быстрая вставка, удаление и поиск: O(1) в среднем.

Ключи должны быть хэшируемыми и неизменяемыми (например, строки, числа, кортежи).

Порядок элементов сохраняется (начиная с Python 3.7).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🖥 Где будет быстрее поиск, а где перебор и почему: dict, list, set, tuple

Поиск будет быстрее в dict и set, потому что это хэш-таблицы, доступ к элементу которых выполняется за O(1). Для list и tuple поиск будет выполняться в среднем за O(n).

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

В Python 2 методы словаря keys, values, items возвращают список. Тоесть перед итерацией по словарю (или сету) интерпретатор сначала создает новый список, что занимает дополнительное время и память, но после создания это уже обыкновенный список. Тоесть в Python 2 итерация по словарям и сетам выполняется дольше, за счет создания нового списка и копирования в него элементов.

В Python 3 эти методы создают объект-представление. Это определенно происходит быстрее чем создание нового списка в Python2. Но итерирование по такому представлению должно происходить немного дольше, чем по списку из-за того что данные в словарях хранятся разреженно. В подтверждение вышесказанного (Python 3):

>>> l = list(range(1000000))
>>> d = dict.fromkeys(l)
>>> s = set(l)
>>> def iter_list():
... for i in l:
... pass
...
>>> def iter_dict():
... for i in d:
... pass
...
>>> def iter_set():
... for i in s:
... pass
...
>>> timeit.timeit(iter_list, number=1000)
6.727667486004066
>>> timeit.timeit(iter_dict, number=1000)
9.293120226997416
>>> timeit.timeit(iter_set, number=1000)
8.627948219014797
Please open Telegram to view this post
VIEW IN TELEGRAM
3
🖥 Как передаются значения аргументов в функцию или метод

В таких языках как C++ есть переменные, хранящиеся на стеке и в динамической памяти. При вызове функции мы помещаем все аргументы на стек, после чего передаём управление функции. Она знает размеры и смещения переменных на стеке, соответственно может их правильно интерпретировать. При этом у нас есть два варианта: скопировать на стек память переменной или положить ссылку на объект в динамической памяти (или на более высоких уровнях стека). Очевидно, что при изменении значений на стеке функции, значения в динамической памяти не поменяются, а при изменении области памяти по ссылке, мы модифицируем общую память, соответственно все ссылки на эту же область памяти «увидят» новое значение.

В python отказались от подобного механизма, заменой служит механизм связывания (assignment) имени переменной с объектом, например при создании переменной: var = "john"

Интерпретатор создаёт объект "john" и «имя» var, а затем связывает объект с данным именем. При вызове функции, новых объектов не создаётся, вместо этого в её области видимости создаётся имя, которое связывается с существующим объектом. Но в python есть изменяемые и неизменяемые типы. Ко вторым, например, относятся числа: при арифметических операциях существующие объекты не меняются, а создаётся новый объект, с которым потом связывается существующее имя. Если же со старым объектом после этого не связано ни одного имени, оно будет удалено с помощью механизма подсчёта ссылок. Если же имя связано с переменной изменяемого типа, то при операциях с ней изменяется память объекта, соответственно все имена, связанные с данной областью памяти «увидят» изменения.
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1
🖥 Что такое итератор?

Итератор (iterator) – это объект, который представляет поток данных. Повторяемый вызов метода __next__() (next() в Python 2) итератора или передача его встроенной функции next() возвращает последующие элементы потока.

Если больше не осталось данных, выбрасывается исключение StopIteration. После этого итератор исчерпан и любые последующие вызовы его метода __next__() снова генерируют исключение StopIteration.

Итераторы обязаны иметь метод __iter__, который возвращает сам объект итератора, так что любой итератор также является итерабельным объектом и может быть использован почти везде, где принимаются итерабельные объекты.

Итераторы представлены абстрактным классом collections.abc.Iterator:

class Iterator(Iterable):

__slots__ = ()

@abstractmethod
def __next__(self):
'Return the next item from the iterator. When exhausted, raise StopIteration'
raise StopIteration

def __iter__(self):
return self

@classmethod
def __subclasshook__(cls, C):
if cls is Iterator:
return _check_methods(C, '__iter__', '__next__')
return NotImplemented


__next__ возвращает следующий доступный элемент и вызывает исключение StopIteration, когда элементов не осталось. __iter__ возвращает self. Это позволяет использовать итератор там, где ожидается итерируемых объект, например for. __subclasshook__ проверяет наличие у класса метода __iter__ и __next__.
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍3🤩2
👩‍💻 Monkey patching в Python: правим чужой код на лету

Нужно изменить поведение библиотеки, не трогая её исходники? Используй monkey patching — подмена функций и методов прямо во время выполнения.

📦 Сценарий: хотим переопределить requests.get, чтобы логировать каждый вызов.
pip install requests


🔧 До патча:
import requests

response = requests.get("https://httpbin.org/get")
print(response.status_code)

➡️ Всё работает стандартно. Теперь хотим влезть и изменить поведение requests.get.

🐒 Monkey patch:
import requests

_original_get = requests.get

def patched_get(*args, **kwargs):
print("📡 Вызов requests.get с аргументами:", args, kwargs)
return _original_get(*args, **kwargs)

requests.get = patched_get

# Тест
response = requests.get("https://httpbin.org/get")
print(response.status_code)

➡️ Теперь каждый вызов requests.get будет логироваться, без изменения кода библиотеки.

🧱 Работает и с классами:
import datetime

# Переопределим now(), чтобы всегда возвращать фиксированную дату
datetime_original = datetime.datetime

class PatchedDateTime(datetime.datetime):
@classmethod
def now(cls, tz=None):
return cls(2000, 1, 1)

datetime.datetime = PatchedDateTime

print(datetime.datetime.now()) # 2000-01-01 00:00:00

🛑 Важно:
Monkey patch — это хак. Используй его, если:

🟢Нельзя изменить библиотеку напрямую
🟢Нужно протестировать edge-кейсы
🟢Нужно быстро перекрыть поведение (в тестах, CI, временных обходах)

🗣️Запомни: Хочешь сделать patch только в рамках with? Тогда уже нужен unittest.mock.patch.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍43👏2
⛓️ "Data pipeline" без Airflow: генераторы + декораторы = mini-ETL

Airflow, Prefect, Luigi? Необязательно. В Python можно собрать пайплайн без всего этого — просто на генераторах и декораторах. Поток, шаги, трансформации — всё своё.

📥 extract: читаем строки из файла

def extract_lines(path):
with open(path) as f:
for line in f:
yield line.strip()

➡️ Классический источник: по одной строке. Работает с любыми размерами файла.

🔄 @transform: оборачиваем функцию в шаг пайплайна

def transform(fn):
def wrapper(source):
for item in source:
yield fn(item)
return wrapper

📌 Каждое значение проходит через fn, результат — снова генератор.

@transform
def to_int(x):
return int(x)

@transform
def square(x):
return x * x

@transform
def add_1(x):
return x + 1

➡️ Каждый шаг можно использовать повторно, в любом порядке.

🧹 Добавим фильтр: только чётные

def filter_even(source):
for item in source:
if item % 2 == 0:
yield item

📎 Фильтр не декоратор — просто генератор-фильтр, встраивается как промежуточный шаг.

📤 Финал: загружаем в список

def load_to_list(source):
return list(source)

➡️ Можно заменить на отправку в БД, файл, API — не важно. Главное — последняя точка.

📡 Сборка пайплайна

stream = extract_lines("numbers.txt")
stream = to_int(stream)
stream = square(stream)
stream = add_1(stream)
stream = filter_even(stream)
result = load_to_list(stream)

print(result)

➡️ Пошаговая сборка. Каждый шаг получает генератор, возвращает генератор.

🧪 Пример файла numbers.txt

1
2
3


➡️ Пройдёт так:

🟢 1 → int → 1 → квадрат = 1 → +1 = 2 → чётное → ОК
🟢 2 → 2 → 4 → 5 → нечётное →
🟢 3 → 3 → 9 → 10 → чётное → ОК

👉 Результат:

[2, 10]


🔍 А если нужно дебажить поток?

def debug(tag):
def inner(source):
for item in source:
print(f"{tag}: {item}")
yield item
return inner

➡️ Вставь stream = debug("AFTER SQUARE")(stream) — и увидишь, что происходит на каждом шаге.


🗣️ Запомни: Пайплайн — это просто поток + операции. Генераторы передают данные. Декораторы превращают функции в шаги. Получается честный mini-ETL, без DAG, UI и брокеров. Всё под контролем. Всё Python.
Please open Telegram to view this post
VIEW IN TELEGRAM
3🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
👩‍💻 Python. Зачем нужны функции?

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


➡️ Ссылка на первоисточник

🤩 Pytstart || #Видеокурс
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍2
🎭 Fake modules: подделываем импорт, чтобы перехватить сторонние зависимости

В Python можно подменить любой импорт. Любой. Всё, что нужно — засунуть объект в sys.modules до того, как кто-то сделает import.

🧨 Подменяем requests
import sys

class FakeRequests:
def get(self, url):
return type("Response", (), {"text": "[FAKE DATA]"})

sys.modules["requests"] = FakeRequests()

➡️ Теперь любой import requests получит подделку. Даже чужая библиотека не узнает, что её обманули.

import requests

print(requests.get("https://example.com").text)

➡️ Вывод:
[FAKE DATA]

📎 HTTP-запроса не было. Всё ушло в фейковый .get().

💡 Подмена json
import types

fake_json = types.ModuleType("json")
fake_json.dumps = lambda obj: "'not json'"
fake_json.loads = lambda s: {"msg": "fake"}

import sys
sys.modules["json"] = fake_json

➡️ Теперь import json возвращает этот фейковый модуль. Можно заменить поведение стандартных функций.

import json

print(json.dumps({"hello": "world"}))
print(json.loads("whatever"))


➡️ Вывод:
'not json'
{'msg': 'fake'}

📎 Никакой сериализации — просто заглушка.

🔒 Блокируем доступ к базе
class BlockedPsycopg:
def connect(self, *args, **kwargs):
raise RuntimeError("DB access blocked")

sys.modules["psycopg2"] = BlockedPsycopg()

➡️ Теперь любой код, который попытается импортировать psycopg2, словит ошибку. Без вариантов.


🗣️ Запомни: sys.modules — это карта всех импортов. Поменяешь объект в ней — поменяешь поведение на всём проекте. Даже стандартные модули можно обмануть.
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍2🔥1👏1
Посты на какие темы вы хотели бы увидеть на нашем канале? Пишите в комметариях.
5👍1
👩‍💻 Code as data: когда Python модифицирует сам себя

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

📖Читаем и модифицируем файл как обычный текст
with open("script.py", "r") as f:
code = f.read()

code = code.replace("DEBUG = False", "DEBUG = True")

with open("script.py", "w") as f:
f.write(code)

📎 Модифицируем строку прямо в исходнике — простой пример "самоизменяющегося" кода

⚙️ Вставка нового кода в файл
snippet = "\nprint('🐍 Я добавлен автоматически')\n"

with open("target.py", "a") as f:
f.write(snippet)

📌Добавляем в конец другой скрипт: автогенерация поведения без ручного редактирования

🧠 Используем exec() для динамического выполнения
code = """
def dynamic_func():
print("Hello from dynamic code!")
"""

exec(code)
dynamic_func()

📎 exec позволяет выполнить строку с кодом — она "оживает" во время выполнения

Теперь немного глубже. Не просто текст — а синтаксическое дерево кода.

🌳 AST: работа с кодом как со структурой
import ast

tree = ast.parse("x = 2\ny = x + 3")

for node in ast.walk(tree):
print(type(node).__name__)

➡️ ast.parse() превращает код в дерево объектов: ты можешь его анализировать или менять

✍️ Изменим Python-код программно
import ast
import astor

code = "x = 2\ny = x + 3"
tree = ast.parse(code)

tree.body.append(ast.parse("print(y)").body[0])
new_code = astor.to_source(tree)

print(new_code)

➡️ Добавили print(y) в конец кода — это уже настоящая трансформация кода на лету

🗣️Запомни: Код = данные. Python может переписать себя, другой скрипт или сгенерировать поведение на ходу.Используй ast, если хочешь безопасность и контроль. exec() — мощно, но опасно.
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍2🔥1😐1
🧬 Taint tracking в Python: отслеживаем путь пользовательских данных

Когда ты получаешь ввод от пользователя — это не просто строка, это потенциальный вектор атаки. Важно отслеживать такие значения и не допускать их в критические операции (shell, SQL, шаблоны) без фильтрации.

Решение — taint tracking: пометка «заражённых» данных и распространение этого статуса в коде.

🔧 Подкласс str с флагом заражения
class TaintedStr(str):
def __new__(cls, content, tainted=True):
obj = super().__new__(cls, content)
obj.tainted = tainted
return obj

def __add__(self, other):
other_taint = getattr(other, "tainted", False)
return TaintedStr(super().__add__(other), self.tainted or other_taint)

def strip(self):
return TaintedStr(super().strip(), self.tainted)

def lower(self):
return TaintedStr(super().lower(), self.tainted)

📌 Что происходит?
Мы создаём обёртку вокруг str, которая сохраняет флаг tainted=True, и автоматически переносит его при операциях (+, strip, lower и т.д.).

🧪 Пример: данные от пользователя
user_input = TaintedStr(" DROP TABLE users; ")

print(user_input) # DROP TABLE users;
print(user_input.tainted) # True

➡️Ввод от пользователя сразу помечается как tainted. Это значение нельзя безопасно использовать без фильтрации.

🔁 Обработка не убирает taint
cleaned = user_input.strip().lower()
print(cleaned) # drop table users;
print(cleaned.tainted) # True

➡️ Даже после strip() и lower() строка остаётся «заражённой». Это важно: механическая очистка не гарантирует безопасность.

🚫 Блокируем опасные вызовы
def run_shell(command):
if getattr(command, "tainted", False):
raise RuntimeError("Заблокировано: команда содержит внешние данные.")
print("OK:", command)

run_shell(cleaned) # RuntimeError

➡️ Перед выполнением проверяем: если строка tainted, выбрасываем исключение. Это защищает от инъекций.

Очистка и сброс флага вручную
def sanitize(value):
return TaintedStr(value.strip(), tainted=False)

trusted = sanitize(user_input)
run_shell(trusted) # OK

➡️ Санитизация — это ручной контроль. Только ты решаешь, что считать безопасным. После проверки можно сбросить флаг.

🧱 Taint распространяется при конкатенации
q1 = TaintedStr("SELECT * FROM users WHERE name = '", tainted=False)
q2 = TaintedStr("admin'; DROP TABLE users;", tainted=True)
q3 = TaintedStr("';", tainted=False)

query = q1 + q2 + q3
print(query.tainted) # True

➡️Если хоть один компонент заражён — результат тоже tainted. Это основной принцип распространения.

🔄 Автоматическая передача через функции
def taint_propagates(fn):
def wrapper(*args, **kwargs):
tainted = any(getattr(arg, "tainted", False) for arg in args)
result = fn(*args, **kwargs)
if isinstance(result, str):
return TaintedStr(result, tainted)
return result
return wrapper

@taint_propagates
def build_path(username):
return f"/home/{username}"

➡️ Оборачиваем функцию: если на входе есть tainted, и возвращается строка — помечаем результат как tainted.

🧪 Проверка перед шаблонизацией
def render_template(tmpl, context):
if getattr(tmpl, "tainted", False):
raise RuntimeError("Нельзя рендерить шаблон с внешними данными")
return tmpl.format(**context)

safe = TaintedStr("Hello, {name}", tainted=False)
danger = TaintedStr("{name}", tainted=True)

render_template(safe, {"name": "Alice"}) # OK
render_template(danger, {"name": "Alice"}) # RuntimeError


🗣️ Запомни: TaintedStr помогает отслеживать путь данных от ввода до критичных точек.
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍1
⚙️ Топ‑5 фреймворков Python для веба и API

Веб‑разработка на Python активно развивается: появляются новые инструменты, улучшающие производительность, асинхронность и удобство. Хочешь всегда оставаться в тренде? Эта статья для тебя!

В статье ты узнаешь:

📌 Рост популярности FastAPI и Sanic — асинхронные фреймворки становятся стандартом для real-time и микросервисов.
📌 Почему Django не сдаёт позиции — стабильность, экосистема и универсальность.
📌 Знакомство с новыми фреймворками — Piccolo, Starlite, Responder — для специфичных задач и старта проектов.
📌 Интеграция AI и ML — фреймворки становятся ближе к аналитике и ИИ функционалу
📌 Советы по выбору: когда лучше асинхронность, а когда — проверенные монолиты.

➡️ Читайте и наслаждайтесь

🗣️ Следующий год за асинхронным, быстрым и AI‑ориентированным вебом. Выбери фреймворк, который подходит именно твоим задачам!

🤩 Pytstart || #Статья
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍5
📖 Парсим логи Nginx с нуля — вытаскиваем нужные данные

Nginx пишет логи вот в таком виде (по умолчанию):
192.168.1.5 - - [10/Jun/2025:10:42:17 +0000] "GET /index.html HTTP/1.1" 200 123 "-" "curl/7.68.0"

➡️ Хочешь из этого вытянуть IP, путь, статус и метод запроса?
Реальный парсинг, без grep, на Python.

🧩 Регулярка для вытаскивания данных
import re

log_line = '''192.168.1.5 - - [10/Jun/2025:10:42:17 +0000] "GET /index.html HTTP/1.1" 200 123 "-" "curl/7.68.0"'''

pattern = re.compile(
r'(?P<ip>\d+\.\d+\.\d+\.\d+)\s.*?"(?P<method>GET|POST|PUT|DELETE)\s(?P<path>/\S*)\sHTTP/[\d.]+"\s(?P<status>\d{3})'
)

match = pattern.search(log_line)
print(match.groupdict())


📌 Вывод:
{'ip': '192.168.1.5', 'method': 'GET', 'path': '/index.html', 'status': '200'}

➡️ Теперь можно делать с этими данными что угодно.

🔍 Ищем только ошибки (5xx)
with open("/var/log/nginx/access.log") as f:
for line in f:
m = pattern.search(line)
if m and m["status"].startswith("5"):
print(f'{m["ip"]} → {m["status"]} на {m["path"]}')

➡️ Увидишь, кто вызывает ошибки сервера.

📊 Подсчёт самых активных IP
from collections import Counter

ips = []

with open("/var/log/nginx/access.log") as f:
for line in f:
m = pattern.search(line)
if m:
ips.append(m["ip"])

for ip, count in Counter(ips).most_common(5):
print(f"{ip} — {count} запросов")

➡️ Можно быстро увидеть, кто долбит твой сервер чаще всех.

⏱️ Парсим $request_time (если ты включил в лог формат)

Если ты прописал $request_time в log_format, строка лога может выглядеть так:
192.168.1.5 - - [10/Jun/2025:10:42:17 +0000] "GET /api/data HTTP/1.1" 200 345 "-" "-" 1.245


pattern = re.compile(
r'(?P<ip>\d+\.\d+\.\d+\.\d+).*?"(?P<method>\w+)\s(?P<path>\S+).*"\s(?P<status>\d+).*\s(?P<time>\d+\.\d+)$'
)

with open("/var/log/nginx/access.log") as f:
for line in f:
m = pattern.search(line)
if m and float(m["time"]) > 1.0:
print(f"{m['path']} — медленно: {m['time']} сек")

➡️ Находишь тормозящие эндпоинты прямо из логов.


🗣️ Запомни: Работать с логами — это не просто искать 404. Это реальный способ анализировать поведение пользователей и состояние бэкенда. Через Python ты можешь фильтровать, группировать, считать и расследовать, не поднимая ни ELK, ни Grafana.
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍3🔥2👏1
⚡️ Async/await в Python: почему это суперсила, а не боль

Если ты до сих пор считал asyncio сложным или ненужным, самое время пересмотреть своё мнение. Совсем недавно в Medium вышла статья, где автор делится, как асинхронный Python перестал быть загадкой и стал мощным инструментом.

В статье ты узнаешь:

📌 Как преодолеть страх перед async def и await — автор рассказывает о моменте, когда это перестало быть "странно" и стало легко.
📌 Почему asyncio идеален для API-heavy микросервисов на FastAPI, обрабатывающих десятки запросов одновременно.
📌 Конкретные примеры кода: chaining, gather, create_task, использование семафоров и очередей.
📌 Совет, как подружиться с этим стилем программирования, получить плавный и отзывчивый сервис.

➡️ Читайте и наслаждайтесь

🗣 Async Python — это когда твой сервер не "ждёт", а работает нон-стопом. Если ты пишешь backend, научиться этому — мастхэв.

🤩 Pytstart || #Статья
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1😭1
🔧 Как использовать background‑задачи в FastAPI

Если ты разрабатываешь Python‑бэкенд на FastAPI и сталкиваешься с долгими операциями — такими как отправка email, генерация отчетов или обработка файлов — BackgroundTasks станет твоим секретным оружием.

В статье ты узнаешь:

📌 Что это делает: запускает функции после отправки ответа, не блокируя API.
📌 Простой пример:
@app.post("/process")
async def process(data: str, background_tasks: BackgroundTasks):
background_tasks.add_task(save_to_db, data)
return {"status": "accepted"}

📌 Плюсы: моментальный отклик, плавная работа сервера.
📌 Минусы: задачи выполняются в том же процессе — для тяжёлых операций лучше использовать Celery.

➡️ Читайте и наслаждайтесь

🗣️ Теперь BackgroundTasks — не просто “полезный бонус”, а ключ — к быстрому, отзывчивому API.

🤩 Pytstart || #Статья
Please open Telegram to view this post
VIEW IN TELEGRAM
4
🎶🖼 Генерация музыки и изображений с помощью Python

Python умеет не только считать, но и создавать — музыку, звуки, картинки, абстракции, даже MIDI и визуализации.

🎵 Пример: генерация музыки с mido и pygame.midi

Создадим простую MIDI-мелодию прямо из Python.
from mido import Message, MidiFile, MidiTrack

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)

notes = [60, 62, 64, 65, 67, 69, 71, 72] # До-мажор

for note in notes:
track.append(Message('note_on', note=note, velocity=64, time=120))
track.append(Message('note_off', note=note, velocity=64, time=120))

mid.save('scale.mid')

➡️ Получишь scale.mid, который можно открыть в любом MIDI-плеере или DAW (FL Studio, GarageBand).

📌 MIDI — это просто команды: нота, скорость, длительность. А mido даёт к ним простой Python-интерфейс.

🖼 Генерация изображений с Pillow и шумом

Создадим абстрактную картинку — просто как визуальное искусство.
from PIL import Image
import random

img = Image.new('RGB', (256, 256))
pixels = img.load()

for x in range(256):
for y in range(256):
r = random.randint(0, 255)
g = (x + y) % 256
b = (x * y) % 256
pixels[x, y] = (r, g, b)

img.save('abstract.png')

➡️ Получаешь абстрактный шум-калейдоскоп. Можно менять формулы, добавлять фильтры, слои.

💡 Генерация через turtle — рисование как в логотипе
import turtle
t = turtle.Turtle()
t.speed(0)

for i in range(360):
t.forward(i * 0.5)
t.right(59)

turtle.done()

➡️ Простая генеративная графика. Идеально для образовательных визуализаций.

🔥 Хочешь мощнее? Используй:

👍 🎨 matplotlib + Perlin noise — для фракталов, текстур, геометрии
👍 🎹 music21, scamp, pretty_midi — для анализа и генерации партитур
👍 🧠 Magenta, DeepDream, VQGAN+CLIP — генерация с помощью ML


🗣️ Запомни: Python — это не только для бэкенда и ML. Он может сочинять музыку и рисовать, если ты дашь ему идею.Начни с MIDI и Pillow, а потом двинься в сторону генеративного искусства и нейронных моделей. Всё в твоих руках.
Please open Telegram to view this post
VIEW IN TELEGRAM
3❤‍🔥2👍2
🧮 Float в Python: == работает не так, как ты думаешь

Ты написал:
if accuracy == 0.9:
do_something()

Но do_something() не вызвался, хотя ты уверен: accuracy был 0.9.

🐍 Python тебе ничего не скажет. А причина — в том, как устроены числа с плавающей точкой.

📉 Пример: числа вроде равны, но == даёт False
a = 0.1 + 0.2
b = 0.3

print(a) # 0.30000000000000004
print(b) # 0.3
print(a == b) # False

➡️ Почему? Потому что 0.1 и 0.2 не могут быть точно представлены в двоичной системе. Они чуть-чуть больше или меньше, чем ты думаешь.

Как сравнивать числа правильно?

1. Через `math.isclose()`
import math

a = 0.1 + 0.2
b = 0.3

if math.isclose(a, b, rel_tol=1e-9):
print("Они почти равны ")

🟢 rel_tol — относительная погрешность (доля от значения)
🟢 по умолчанию 1e-9, но можно настраивать

2. Или вручную через `abs()`
if abs(a - b) < 1e-9:
print("Тоже нормально ")


🧪 Практика: где float ломает логику

👍 Проверка метрики модели
accuracy = 0.89999999999999

if accuracy == 0.9:
print("Готово!") # не сработает


✔️ Надо:
if math.isclose(accuracy, 0.9, rel_tol=1e-3):
print("Готово!") #


👍 Юнит-тесты в ML/науке
def test_calc():
assert (0.1 + 0.2) == 0.3 # сломается


✔️ Надо так:
def test_calc():
assert math.isclose(0.1 + 0.2, 0.3) #


👍 Фильтрация по значению
data = [0.1 + 0.2, 0.3, 0.4]
filtered = [x for x in data if x == 0.3]
print(filtered) # пусто


✔️ Сравни с допуском:
filtered = [x for x in data if math.isclose(x, 0.3)]
print(filtered) # [0.30000000000000004, 0.3]


⚠️ Не путай с Decimal

Если тебе нужна точная математика, например для денег:
from decimal import Decimal

print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3")) # True

➡️ Но в ML, NumPy и TensorFlow используется float — поэтому важно уметь его правильно сравнивать.


🗣️ Запомни:Никогда не делай float1 == float2.Используй math.isclose() или abs(a - b) < ε.
Это не придирка — это баг, который может сидеть тихо в проде, в тестах, в логике. Всегда проверяй float сравнение глазами.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍84🔥3