🚦 Feature Flags и Canary: как Python выпускает код без боли
Никто не хочет падений после деплоя. Feature Flags и Canary-развёртывания позволяют включать фичи по частям: сначала 1%, потом 10%, потом всем. Без ручных выкатываний и откатов.
💀 Старый путь — “всё или ничего”
➡️ Фича включается для всех сразу. Если падает — падает всё.
⚙️ Добавляем флаг
➡️ Теперь можно включать фичи точечно, без релиза.
🧩 Храним флаги в Redis
➡️ Изменил флаг в Redis — поведение поменялось в реальном времени.
🧠 Canary rollout
➡️ Только 10% пользователей получают новую версию. Остальные — стабильный код.
📊 Метрики на фичу
➡️ Мгновенно видишь, как влияет новая логика.
⚡️ Canary через прокси
NGINX split-трафик по cookie или hash:
➡️ Управляешь выкаткой без касания Python-кода.
🧰 Комбинация с CI/CD
➡️ Новая фича включается автоматически после деплоя.
🔥 Canary + Celery
➡️ Даже фоновые задачи можно тестировать на части нагрузки.
🔍 Rollback за секунды
➡️ Фича отключается без перезапуска.
🚀 Full rollout
➡️ Всё прошло — включаем всем. Без deploy, без боли.
🗣 Запомни: флаги — это не костыль, а ремни безопасности. Python с Feature Flags и Canary учит выкатывать код медленно, зато без катастроф.
Никто не хочет падений после деплоя. Feature Flags и Canary-развёртывания позволяют включать фичи по частям: сначала 1%, потом 10%, потом всем. Без ручных выкатываний и откатов.
if new_feature_enabled:
process_new_logic()
else:
process_old_logic()
from featureflags import FeatureFlags
flags = FeatureFlags({
"new_ui": False,
"smart_sort": True
})
if flags.is_enabled("smart_sort"):
sort_items_ml()
else:
sort_items_old()
🧩 Храним флаги в Redis
import redis, json
r = redis.Redis()
r.set("feature_flags", json.dumps({"beta_chat": True}))
flags = json.loads(r.get("feature_flags"))
if flags.get("beta_chat"):
launch_beta_chat()
import random
def canary(percent: int):
return random.randint(1, 100) <= percent
if canary(10):
serve_new_api()
else:
serve_old_api()
def process():
if flags["smart_sort"]:
metrics.increment("sort.smart.active")
else:
metrics.increment("sort.old.active")
NGINX split-трафик по cookie или hash:
if ($cookie_user_group = "canary") {
proxy_pass http://new-version;
}# .github/workflows/deploy.yml
- name: Toggle feature flag
run: curl -X POST http://flags/api/enable?flag=smart_sort
if canary(5):
queue = "tasks_beta"
else:
queue = "tasks_prod"
send_task(queue, "process_data")
r.set("feature_flags", json.dumps({"smart_sort": False}))flags["new_ui"] = True
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍1🔥1
CLI уже давно не должен быть серым текстом на чёрном фоне.
rich и prompt_toolkit превращают Python-консоль в полноценный интерфейс: цвет, подсветка, ввод, меню — всё живое и интерактивное.💀 Старый CLI — мёртвый текст
name = input("Введите имя: ")
print("Привет,", name)from rich import print
from rich.console import Console
console = Console()
console.print("[bold green]Hello, world![/bold green]")
from rich.progress import track
for step in track(range(5), description="Processing..."):
time.sleep(0.5)
from rich.table import Table
table = Table(title="Users")
table.add_column("Name")
table.add_column("Age")
table.add_row("Alice", "24")
table.add_row("Bob", "30")
console.print(table)
from prompt_toolkit import prompt
from prompt_toolkit.completion import WordCompleter
langs = WordCompleter(["Python", "Go", "Rust"])
name = prompt("Язык? ", completer=langs)
print("Вы выбрали:", name)
from prompt_toolkit.lexers import PygmentsLexer
from prompt_toolkit.styles import Style
from pygments.lexers.python import PythonLexer
prompt(">>> ", lexer=PygmentsLexer(PythonLexer), style=Style.from_dict({"": "#00ff00"}))
from prompt_toolkit.shortcuts import checkboxlist_dialog
result = checkboxlist_dialog(
title="Выбор модулей",
text="Отметьте нужные:",
values=[("auth", "Auth"), ("db", "Database"), ("api", "API")]
).run()
print(result)
from rich.console import Console
from prompt_toolkit import PromptSession
console = Console()
session = PromptSession()
while True:
cmd = session.prompt(">>> ")
console.print(f"[cyan]Выполняю:[/cyan] {cmd}")
from prompt_toolkit import Application
from prompt_toolkit.key_binding import KeyBindings
kb = KeyBindings()
@kb.add("c-c")
def _(event): event.app.exit()
app = Application(full_screen=True, key_bindings=kb)
app.run()
from rich.logging import RichHandler
import logging
logging.basicConfig(level="INFO", handlers=[RichHandler()])
logging.info("Система запущена")
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8❤🔥2👍1
Python — удобный, но медленный. WebAssembly (WASM) даёт шанс выжать из него максимум: код работает в браузере или sandbox-среде со скоростью C, при этом безопасно и кроссплатформенно.
def fib(n):
return n if n < 2 else fib(n-1) + fib(n-2)
print(fib(35))
⚙️ Компиляция в WebAssembly
pip install pyodide-build
pyodide build
🌐 Запуск Python в браузере
<script type="module">
import { loadPyodide } from "https://cdn.jsdelivr.net/pyodide/v0.26.0/full/pyodide.mjs";
const pyodide = await loadPyodide();
await pyodide.runPythonAsync("print('Hello from WebAssembly!')");
</script>
⚡️ Ускоряем вычисления через Rust
#[no_mangle]
pub extern "C" fn square(x: i32) -> i32 {
x * x
}
.wasm, а Python вызывает его как обычную функцию.🔗 Подключаем WASM в Python
import wasmtime
store = wasmtime.Store()
module = wasmtime.Module.from_file(store.engine, "math.wasm")
instance = wasmtime.Instance(store, module, [])
square = instance.exports(store)["square"]
print(square(store, 12))
🧩 Лёгкая изоляция
WebAssembly — песочница:
🟢 нельзя обращаться к файлам,🟢 нельзя падать с segfault,🟢 код полностью детерминирован.
wasmer run app.wasm
import numpy as np
a = np.arange(1e6)
print(a.sum())
Просто кидаешь .wasm в CDN, и Python-код выполняется где угодно.
from flask import Flask
from wasmtime import Store, Module, Instance
app = Flask(__name__)
store = Store()
module = Module.from_file(store.engine, "math.wasm")
instance = Instance(store, module, [])
square = instance.exports(store)["square"]
@app.route("/square/<int:x>")
def api_square(x):
return {"result": square(store, x)}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤3🔥1🤔1
Media is too big
VIEW IN TELEGRAM
В этом видео автор показывает, как с нуля создать два практических проекта на Python: простой калькулятор и игру "Угадай число".Разбирается, как реализовать основные операции (сложение, вычитание, умножение, деление), обработать ошибки вроде деления на ноль, и как организовать логику игры с подсказками и проверками.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍2🔥1
Media is too big
VIEW IN TELEGRAM
В этом видео автор показывает, как записывать данные в CSV файлы с помощью модуля csv. Разбирается, как работать с кодировками (особенно проблемы в Windows), управлять разделителями и избегать пустых строк при записи.
Это практический навык, который пригодится при работе с данными, экспортом информации и автоматизации обработки файлов.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7❤2
Многие новички (и даже опытные разработчики) забывают, что в Python блок
else можно использовать вместе с циклами for и while.Блок
else выполняется только в том случае, если цикл завершился естественным образом (то есть полностью прошел все итерации и не был прерван оператором break).Это позволяет избавиться от лишних переменных-флагов (вроде
found = False) при поиске элементов.Ищем число в списке. Если нашли — прерываем цикл. Если перебрали всё и не нашли — срабатывает
else.
items = [1, 3, 5, 7]
target = 4
for item in items:
if item == target:
print("Нашел!")
break
else:
# Сработает, только если break НЕ был вызван
print("Элемента нет в списке")
for-else, чтобы сделать код поиска чище и понятнее, убирая лишние проверки после цикла.Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤4🔥3
Забудьте про громоздкие срезы вроде
list[1:-1]. В Python можно использовать оператор * (asterisk), чтобы гибко распаковывать итерируемые объекты.Нужно взять первый и последний элементы списка, а всё, что посередине, сохранить отдельно.
data = [10, 20, 30, 40, 50, 60]
# first — первый элемент
# last — последний
# *middle — всё остальное (соберется в список)
first, *middle, last = data
print(first) # 10
print(last) # 60
print(middle) # [20, 30, 40, 50]
Везде! Списки, кортежи, строки. Звездочка может стоять в начале, в конце или посередине (но только одна в выражении).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤3🥴1
Если вы часто пишете
print("var =", var), чтобы проверить значение переменной, то этот трюк сэкономит вам кучу времени.Начиная с Python 3.8, в f-строках можно использовать знак равно =.
user = "Alex"
age = 25
print(f"user = {user}, age = {age}")
# Вывод: user = Alex, age = 25
Просто добавьте = в конце переменной внутри фигурных скобок. Python сам подставит имя переменной и её значение.
user = "Alex"
age = 25
print(f"{user=}, {age=}")
# Вывод: user='Alex', age=25
Это работает даже с выражениями:
print(f"{2 + 2 = }")
# Вывод: 2 + 2 = 4
Мелочь, а код для отладки пишется в два раза быстрее! 🚀
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤3
Сталкивались с ошибкой
KeyError или писали громоздкие проверки if key not in my_dict, просто чтобы добавить элемент в список внутри словаря?В модуле
collections есть спаситель — defaultdict.Если вы обращаетесь к ключу, которого нет,
defaultdict не выдаст ошибку, а создаст значение по умолчанию сам.Пример: Группируем имена по длине.
⛔ Обычный способ:
names = ["Ana", "Bob", "Alex", "John"]
groups = {}
for name in names:
length = len(name)
if length not in groups:
groups[length] = [] # Создаем список, если ключа нет
groups[length].append(name)
✅ С defaultdict:
from collections import defaultdict
names = ["Ana", "Bob", "Alex", "John"]
# Указываем list как фабрику значений по умолчанию
groups = defaultdict(list)
for name in names:
# Не нужно проверять наличие ключа!
groups[len(name)].append(name)
print(dict(groups))
# {3: ['Ana', 'Bob'], 4: ['Alex', 'John']}
Меньше кода — меньше багов. Идеально для подсчета статистики и группировки данных.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤1
Если вы всё еще склеиваете пути к файлам через строки или
os.path.join, пора переходить на pathlib. Это встроенная библиотека, которая превращает пути в удобные объекты.Пути можно соединять через оператор деления
/! Это выглядит очень чисто и интуитивно.Создадим путь к файлу
data/report.txt в текущей директории.
from pathlib import Path
# Получаем текущую папку
current_dir = Path.cwd()
# Магия слешей! Никаких запятых и скобок
file_path = current_dir / "data" / "report.txt"
# Проверяем, существует ли файл
if file_path.exists():
# Читаем текст сразу методом объекта
print(file_path.read_text())
else:
print("Файл не найден")
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤2
Как проверить, что все элементы в списке положительные? Писать цикл? Нет!
Используйте встроенные функции
all() и any().
numbers = [1, 5, 10, 20]
# all вернет True, только если ВСЕ условия верны
if all(n > 0 for n in numbers):
print("Все числа положительные!")
# any вернет True, если ХОТЯ БЫ ОДНО условие верно
if any(n > 15 for n in numbers):
print("Есть число больше 15!")
Это читается почти как обычный английский текст. Пишите выразительно! ✍️
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10⚡3
Real-time — это не обязательно Kafka, ZooKeeper и зоопарк сервисов.
Redis Streams даёт очередь, автодоставку и группировку воркеров в одном бинарнике.Python делает остальное. Ниже — рабочая схема, которая живёт в проде годами.
import redis
r = redis.Redis()
r.xadd("events:log", {
"level": "info",
"msg": "service started"
})
С этого момента у тебя есть real-time поток.
events = r.xread({"events:log": "$"}, block=0)
for stream, items in events:
for event_id, data in items:
print(event_id, data)$ = читать только новые сообщения.Идеально для live-обработчиков: телеметрия, алертинг, IoT.
try:
r.xgroup_create("events:log", "log_workers", "$")
except:
pass
events = r.xreadgroup(
"log_workers", "worker-1",
{"events:log": ">"},
block=5000
)
> — бери только необработанные записи для группы.Каждое сообщение попадёт ровно одному воркеру.
for event_id, data in events[0][1]:
handle(data)
r.xack("events:log", "log_workers", event_id)
XACK Redis будет думать, что воркер завис.ACK — обязательный шаг любого real-time пайплайна.
🧹 5. Держим только последние N событий
r.xadd("events:log",
{"msg": "slow request"},
maxlen=1000)Идеально для журналов и сенсорных потоков.
producer.py
r.xadd("sensor:data", {
"temp": "22.1",
"humidity": "44"
})worker.py
while True:
events = r.xreadgroup(
"sensors", "worker-3",
{"sensor:data": ">"},
block=3000
)
if not events:
continue
for event_id, data in events[0][1]:
store(data) # запись в БД
r.xack("sensor:data", "sensors", event_id)
Работает быстро, стабильно и без лишних сервисов.
length = r.xlen("sensor:data")
if length > 50000:
throttle() # временно уменьшаем частоту отправки🛰 8. Репликация для отказоустойчивости
Redis Streams прекрасно работает на Redis-кластерe:
replica-of host port
redis-cli xinfo stream sensor:data
redis-cli xinfo groups sensor:data
redis-cli xinfo consumers sensor:data group
Без этого real-time не существует.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5❤3
Сколько здесь нулей:
1000000000? Миллиард? Сто миллионов? Приходится считать пальцем по экрану.Python позволяет использовать знак подчеркивания
_ прямо внутри чисел для разделения разрядов. Интерпретатор просто игнорирует эти знаки.
# Обычная запись
salary = 5000000
# Читаемая запись
salary = 5_000_000 # Сразу видно — 5 миллионов
# Работает и с float, и в hex
pi = 3.141_592_653
color = 0xFF_FF_FF
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12🔥3
Раньше, чтобы объединить два словаря, мы использовали метод
.update() (который меняет исходный словарь) или сложную распаковку {**dict1, **dict2}.Начиная с Python 3.9, появился красивый оператор слияния
| (pipe).
settings_default = {"theme": "light", "notifications": True}
user_settings = {"theme": "dark"}
# Объединяем! Значения из второго словаря перезапишут первые
config = settings_default | user_settings
print(config)
# {'theme': 'dark', 'notifications': True}
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍4🔥2
В большинстве языков (C++, Java, JS), чтобы проверить, находится ли число в диапазоне, нужно писать два условия. В Python всё гораздо изящнее.
❌ Как пишут обычно:
age = 25
if age > 18 and age < 60:
print("Трудоспособный возраст")
✅ Как можно в Python:
age = 25
# Прямо как в математике!
if 18 < age < 60:
print("Трудоспособный возраст")
Это работает с любым количеством операторов:
x = 5
y = 10
z = 15
if x < y < z:
print("Порядок возрастания соблюден!")
and) — меньше.Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3
Допустим, у вас есть список имен и список зарплат. Вам нужно вывести их парами. Не нужно использовать индексы!
В Python есть встроенная функция
zip(), которая "сшивает" списки как молнию на куртке.
names = ["Alice", "Bob", "Charlie"]
salaries = [50000, 60000, 70000]
# name берется из names, salary из salaries
for name, salary in zip(names, salaries):
print(f"{name} зарабатывает {salary}")
С помощью
zip можно мгновенно создать словарь из двух списков:
data_dict = dict(zip(names, salaries))
# {'Alice': 50000, 'Bob': 60000, ...}
Минимум кода — максимум пользы.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤1
Вам нужно узнать, сколько раз каждый символ встречается в строке или сколько раз слово встречается в списке? Не пишите циклы и словари вручную!
В модуле
collections есть идеальный инструмент — Counter.
from collections import Counter
text = "abracadabra"
stats = Counter(text)
print(stats)
# Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
Он работает как словарь, но умеет больше. Например, метод
.most_common() сразу покажет топ элементов:
# Вывести 2 самых частых символа
print(stats.most_common(2))
# [('a', 5), ('b', 2)]
Please open Telegram to view this post
VIEW IN TELEGRAM
:=Странное название, но очень полезная суть. Оператор
:= (он похож на глаза и клыки моржа) позволяет присвоить значение переменной прямо внутри выражения (например, внутри if или while).Задача:
Получить данные от функции (или пользователя) и проверить, не пустые ли они.
❌ Без моржа (3 строки):
text = input("Введите слово: ")
if len(text) > 5:
print(f"Длинное слово: {text}")
✅ С моржом (2 строки):
# Мы и присваиваем text, и сразу проверяем его длину
if len(text := input("Введите слово: ")) > 5:
print(f"Длинное слово: {text}")
Переменная
text останется доступной и внутри блока if, и после него. Это отлично экономит место и делает код компактнее.Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍6
Как запускать десятки сетевых запросов одновременно, не тормозя программу? Писать многопоточный код?
Используйте
async и await с библиотекой asyncio.import asyncio
async def fetch_data(n):
print(f"Запрос {n} стартовал")
await asyncio.sleep(1) # имитация сетевого запроса
print(f"Запрос {n} завершён")
return n * 2
async def main():
# запускаем все задачи одновременно
results = await asyncio.gather(*(fetch_data(i) for i in range(5)))
print("Результаты:", results)
asyncio.run(main())
asyncio.gather запускает все задачи параллельноPlease open Telegram to view this post
VIEW IN TELEGRAM
❤4👍2
Как выполнять математические операции над большими массивами без циклов Python? Писать
for и append? Используйте векторизацию NumPy.
import numpy as np
# создаём массив чисел
a = np.arange(1, 6)
b = np.arange(10, 15)
# сложение массивов поэлементно
c = a + b
print("Сумма массивов:", c)
# умножение каждого элемента на 2
d = a * 2
print("Умножение на 2:", d)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3
Как быстро подсчитать суммы, средние и максимум по категориям без цикла? Писать
for и if? Используйте
groupby и агрегации.import pandas as pd
# создаём DataFrame
data = pd.DataFrame({
"Компания": ["A", "B", "A", "B", "C"],
"Доход": [100, 200, 150, 120, 300],
"Расход": [50, 80, 60, 70, 150]
})
# группируем по компании и считаем суммарный доход и средний расход
summary = data.groupby("Компания").agg({
"Доход": "sum",
"Расход": "mean"
})
print(summary)
groupby позволяет группировать данные по ключуagg выполняет несколько операций сразуPlease open Telegram to view this post
VIEW IN TELEGRAM
👍7❤3