en
Feedback
Python академия

Python академия

Open in Telegram

Учи Python быстро и легко. Ежедневно публикуем практические задачи, разборы, готовые решения, объяснения логики, советы по алгоритмам. ✔ Подходит для прокачки навыков кодирования. По всем вопросам @evgenycarter

Show more
7 304
Subscribers
-224 hours
-187 days
-3430 days
Posts Archive
Магия одной строки: List Comprehension Вы всё еще пишете циклы для заполнения списков вот так?

squares = []
for x in range(10):
    squares.append(x ** 2)

Это работает, но это "старая школа". В Python есть способ сделать это элегантнее, быстрее и понятнее - Списковые включения. Как это выглядит: [выражение for элемент in список] Тот же самый код выше превращается в:

squares = [x ** 2 for x in range(10)]

🚀 Почему это круто? 1. Лаконичность: Меньше кода меньше багов. 2. Скорость: Это работает быстрее обычного цикла for, потому что метод .append() не вызывается на каждой итерации (оптимизация на уровне C). Уровень PRO: Добавляем условия Внутри можно использовать if, чтобы фильтровать данные на лету. Задача: Оставить только четные числа и умножить их на 10.

numbers = [1, 2, 3, 4, 5, 6]

# Читается как английское предложение:
# "x умножить на 10 для каждого x в numbers, если x четный"
result = [x * 10 for x in numbers if x % 2 == 0]

print(result) 
# [20, 40, 60]

💡 Совет: Не увлекайтесь! Если ваше выражение не влезает в одну строку или становится слишком сложным (например, вложенные циклы), лучше вернитесь к старому доброму for. Читаемость важнее понтов! Часто используете эту конструкцию? Ставьте 🔥! 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

⚡ Lambda: Функции-однострочники В Python часто бывают ситуации, когда вам нужна маленькая функция всего один раз. Например, ч
Lambda: Функции-однострочники В Python часто бывают ситуации, когда вам нужна маленькая функция всего один раз. Например, чтобы отсортировать список по хитрому правилу. Писать для этого полноценный def, придумывать имя и занимать 3 строки кода - лень и некрасиво. Тут на сцену выходят Lambda-функции (они же анонимные функции). Синтаксис прост: lambda аргументы: выражение Сравните:

# Классический способ
def square(x):
    return x ** 2

# Lambda-стиль
sq = lambda x: x ** 2

🔥 Где это реально нужно? Чаще всего - внутри функций sorted(), min(), max(), map() или filter(). Пример из жизни: У нас есть список студентов и их баллов. Нам нужно отсортировать их не по имени, а по баллам.

students = [
    ("Антон", 50),
    ("Вика", 90),
    ("Борис", 75)
]

# Сортируем по 2-му элементу кортежа (x[1])
sorted_students = sorted(students, key=lambda x: x[1])

print(sorted_students)
# [('Антон', 50), ('Борис', 75), ('Вика', 90)]

Внутри key= мы на лету создали функцию, которая принимает студента x и возвращает его балл x[1]. sorted использует это значение для сортировки. 🛑 Когда НЕ надо использовать Lambda: Не присваивайте лямбды переменным, если планируете использовать их много раз.

# ❌ Плохо (нарушает PEP 8):
f = lambda x: x * 2

# ✅ Хорошо:
def f(x):
    return x * 2

Лямбды созданы, чтобы быть "одноразовыми"! Используете лямбды или предпочитаете старый добрый def? 👇 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

Магия Python: Как работают декораторы? Вы постоянно видите их в коде: @bot.message_handler, @app.route, @staticmethod. Но что на самом деле делает этот символ @? В Python функции - это объекты. Их можно передавать в другие функции, как обычные переменные. Декоратор - это просто функция, которая принимает другую функцию, "обертывает" её в дополнительный код и возвращает обратно. Представьте, что у вас есть подарок (функция). Декоратор - это упаковочная бумага. Подарок внутри тот же, но теперь он выглядит и ведет себя немного иначе. Пример: Декоратор-логгер Допустим, мы хотим автоматически писать в консоль, когда функция запустилась и когда завершилась.

def my_logger(func):
    def wrapper():
        print("📢 Запускаю функцию...")
        func()  # Вызываем оригинальную функцию
        print("✅ Функция отработала!")
    return wrapper

# Применяем декоратор
@my_logger
def say_hello():
    print("Привет, мир!")

# Вызываем
say_hello()

Результат в консоли:

📢 Запускаю функцию...
Привет, мир!
✅ Функция отработала!

🤯 Что происходит под капотом? Синтаксис с @ - это просто "сахар" (упрощение). На самом деле Python делает вот это:

# Это:
@my_logger
def say_hello(): ...

# Равносильно этому:
def say_hello(): ...
say_hello = my_logger(say_hello)

А как это работает в ботах (@dp.message)? В библиотеках типа aiogram декораторы часто используются для регистрации. Когда вы пишете @dp.message(), декоратор не меняет код вашей функции, а сообщает Диспетчеру: "Эй, запомни эту функцию! Если придет сообщение, нужно запустить именно её". Итог: Декораторы нужны, чтобы добавить логику вокруг функции (проверка прав доступа, замер скорости, логирование), не меняя её код внутри. Ставь ❤️, если магия рассеялась и стало понятнее! 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🎛 Делаем бота удобным: Кнопки и Меню Никто не любит вводить команды вручную (/settings, /help). Пользователи любят тыкать пальцем! В Telegram есть два вида кнопок: 1. Reply-кнопки: Находятся под полем ввода. Идеальны для главного меню. 2. Inline-кнопки: Прикреплены к конкретному сообщению. Идеальны для ссылок, лайков или навигации. Давайте добавим оба вида в нашего бота! 🛠 Код:

from aiogram.types import KeyboardButton, InlineKeyboardButton
from aiogram.utils.keyboard import ReplyKeyboardBuilder, InlineKeyboardBuilder

# --- 1. Создаем Reply-клавиатуру (Главное меню) ---
def get_main_menu():
    # Инициализируем билдер
    builder = ReplyKeyboardBuilder()
    
    # Добавляем кнопки
    builder.add(KeyboardButton(text="🔥 Профиль"))
    builder.add(KeyboardButton(text="⚙️ Настройки"))
    builder.add(KeyboardButton(text="📈 Статистика"))
    
    # Настраиваем сетку: 2 кнопки в первом ряду, 1 во втором
    builder.adjust(2, 1)
    
    # resize_keyboard=True делает кнопки компактными
    return builder.as_markup(resize_keyboard=True)

# --- 2. Создаем Inline-клавиатуру (Ссылка) ---
def get_url_keyboard():
    builder = InlineKeyboardBuilder()
    builder.row(InlineKeyboardButton(
        text="↗️ Подписаться на канал", 
        url="https://t.me/pythonofff"
    ))
    return builder.as_markup()

# --- Используем в хэндлере ---
@dp.message(Command("start"))
async def cmd_start(message: types.Message):
    await message.answer(
        "Привет! Вот твое меню 👇", 
        reply_markup=get_main_menu()
    )
    
    await message.answer(
        "А это инлайн-кнопка со ссылкой:",
        reply_markup=get_url_keyboard()
    )

💡 В чем сила Builders? Метод .adjust(2, 1) позволяет одной строчкой настроить "сетку" кнопок. Раньше для этого приходилось писать сложные циклы списков! Сохраняй, чтобы не гуглить синтаксис каждый раз! 💾 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🤖 Твой первый Telegram-бот на aiogram Мы уже выяснили, что асинхронность это круто. А где она используется чаще всего? Конечно, в Telegram-ботах! Сегодня напишем "Эхо-бота" - это как "Hello World" в мире ботов. Он будет просто повторять всё, что мы ему напишем. 🛠 Подготовка: 1. Идем к «отцу всех ботов» - @BotFather. 2. Пишем /newbot, даем имя и юзернейм. 3. Получаем API TOKEN (длинная строка символов). 4. Устанавливаем библиотеку: pip install aiogram 💻 Код (всего 20 строк):

import asyncio
import logging
from aiogram import Bot, Dispatcher, types
from aiogram.filters import CommandStart

# Вставь сюда токен от BotFather
TOKEN = "ВАШ_ТОКЕН_ЗДЕСЬ"

# Включаем логирование, чтобы видеть ошибки в консоли
logging.basicConfig(level=logging.INFO)

# Создаем объекты бота и диспетчера
bot = Bot(token=TOKEN)
dp = Dispatcher()

# 1. Обработчик команды /start
@dp.message(CommandStart())
async def cmd_start(message: types.Message):
    await message.answer("Привет! Я твой первый бот на aiogram 🚀")

# 2. Обработчик любого текста (Эхо)
@dp.message()
async def echo_handler(message: types.Message):
    # Метод .answer() отправляет ответ в тот же чат
    await message.answer(f"Ты написал: {message.text}")

# Запуск процесса опроса (polling)
async def main():
    await dp.start_polling(bot)

if __name__ == "__main__":
    asyncio.run(main())

🧩 Разбор магии: 🟢Dispatcher (dp) - это мозг бота. Он получает сообщения от Telegram и решает, какой функции их передать. 🟢@dp.message(...) - это декораторы. Они "фильтруют" сообщения. 🟢await message.answer(...) - асинхронная отправка сообщения. Пока бот отправляет ответ тебе, он может параллельно обрабатывать сообщения от других пользователей! Скопируй, вставь токен и запусти. Поздравляю, ты только что создал своего бота! 🎉 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🚀 Синхронность vs Асинхронность: Ускоряем код в 10 раз Представьте, что вы стоите в очереди в Макдональдс. 🐢 Синхронный под
🚀 Синхронность vs Асинхронность: Ускоряем код в 10 раз Представьте, что вы стоите в очереди в Макдональдс. 🐢 Синхронный подход (как requests): Кассир принимает ваш заказ, уходит на кухню, ждет, пока пожарится картошка, наливает колу, отдает заказ вам и только потом обращается к следующему человеку в очереди. Медленно? Ужасно медленно! ⚡️ Асинхронный подход (как aiohttp): Кассир принимает заказ, кричит на кухню "Свободная касса!" и сразу берет заказ у следующего, пока ваш готовится. Работа идет параллельно. В Python для этого используется библиотека aiohttp и ключевые слова async / await. Сравним на практике: Допустим, нам нужно скачать 10 страниц.

import asyncio
import aiohttp
import time

# Функция для одного запроса
async def fetch_page(session, url):
    async with session.get(url) as response:
        return response.status

async def main():
    urls = ["https://google.com" for _ in range(10)] # 10 запросов
    
    async with aiohttp.ClientSession() as session:
        tasks = []
        for url in urls:
            # Создаем задачи, но еще не запускаем их ожидание
            tasks.append(fetch_page(session, url))
        
        # Запускаем все задачи одновременно!
        await asyncio.gather(*tasks)

start = time.time()
# Запуск асинхронного цикла
asyncio.run(main()) 
print(f"Время выполнения: {time.time() - start:.2f} сек")

Результат: Если requests будет делать это ~10 секунд (по 1 сек на запрос), то aiohttp справится за ~1.2 секунды. Он просто отправит все 10 запросов разом и будет ждать ответы. 🛠 Установка: pip install aiohttp ⚠️ Важно: Не используйте это для атак на сайты (DDoS). Сервер может забанить вас за слишком большое количество запросов в секунду. Сохраняй, пригодится для парсинга больших объемов данных! 🔥 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🛑 Твой код завис навсегда? Исправляем! Знаете ли вы, что по умолчанию библиотека requests ждет ответа от сервера бесконечно?
🛑 Твой код завис навсегда? Исправляем! Знаете ли вы, что по умолчанию библиотека requests ждет ответа от сервера бесконечно? Если сервер, к которому вы обращаетесь, "упал" или просто завис, ваш Python-скрипт тоже встанет и будет висеть вечно, пока вы не убьете процесс вручную. В продакшене (да и в пет-проектах) это недопустимо! 🚫 ✅ Решение: Всегда используйте timeout Параметр timeout указывает, сколько секунд ждать ответа, прежде чем выбросить ошибку и продолжить работу. Пример безопасного кода:

import requests
from requests.exceptions import Timeout, ConnectionError

url = "https://httpbin.org/delay/5"  # Сервер ответит через 5 сек

try:
    # Ставим таймаут 2 секунды.
    # Если сервер не ответит за 2 сек — скрипт не зависнет!
    response = requests.get(url, timeout=2)
    response.raise_for_status() # Проверка на ошибки 4xx/5xx
    print("Успех:", response.json())

except Timeout:
    print("⏰ Ошибка: Сервер отвечает слишком долго!")
except ConnectionError:
    print("🔌 Ошибка: Нет интернета или сайт недоступен.")
except Exception as e:
    print(f"Что-то пошло не так: {e}")

💡 Совет: Хорошей практикой считается ставить таймаут в диапазоне 3–10 секунд для обычных запросов. Проверьте свои старые проекты, есть ли там timeout? Если нет, самое время добавить! 👨‍💻 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🚀 Ускоряем массовые запросы: Sessions Представьте, что вам нужно скачать 100 страниц сайта. Если делать это через обычный requests.get(), каждый раз будет происходить следующее: 1. Открытие соединения (TCP Handshake). 2. Настройка шифрования (SSL/TLS). 3. Отправка данных. 4. Закрытие соединения. Это очень долго! 🐢 Решение: Session Сессия позволяет переиспользовать одно и то же соединение для множества запросов. Это дает огромный прирост скорости. Пример:

import requests

# Создаем сессию
with requests.Session() as s:
    # Можно сразу настроить заголовки для всех запросов
    s.headers.update({'User-Agent': 'my-app/0.0.1'})

    for i in range(5):
        # Используем s.get вместо requests.get
        r = s.get(f"https://httpbin.org/get?page={i}")
        print(f"Запрос {i} выполнен!")

💡Сессия также запоминает куки (cookies). Если вы авторизовались на сайте в первом запросе, сессия "запомнит" это для следующих. Используйте сессии, если делаете больше двух запросов к одному сайту! 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🛠 Передаем параметры красиво Частая ошибка новичков, склеивать параметры запроса вручную прямо в URL. ❌ Как НЕ надо делать:

user_id = 12
# Плохо: сложно читать, легко ошибиться, проблемы со спецсимволами
url = "https://jsonplaceholder.typicode.com/posts?userId=" + str(user_id)
r = requests.get(url)

Как НАДО делать: Используйте аргумент params. Библиотека сама всё склеит и, что важно, правильно закодирует символы (например, заменит пробелы на %20).

import requests

url = "https://jsonplaceholder.typicode.com/posts"
# Параметры кладем в словарь
my_params = {
    "userId": 1,
    "_limit": 5
}

# Передаем словарь в функцию
response = requests.get(url, params=my_params)

print(response.url) 
# Вывод: .../posts?userId=1&_limit=5

Это делает код чище, безопаснее и профессиональнее. 😎 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🌐 Твой первый API запрос на Python Если нужно получить данные с сайта (курс валют, погоду или список постов), библиотека requests - твой лучший друг. Она делает общение с интернетом невероятно простым. Допустим, мы хотим получить шутку с бесплатного API. Как это сделать в 3 строчки:

import requests

# 1. Делаем GET-запрос по адресу
url = "https://official-joke-api.appspot.com/random_joke"
response = requests.get(url)

# 2. Проверяем, что всё ок (код 200)
if response.status_code == 200:
    # 3. Превращаем ответ из JSON в словарь Python
    data = response.json()
    print(f"Шутка: {data['setup']} - {data['punchline']}")
else:
    print("Ошибка соединения 😢")

📌 Разбор: 🟢.status_code - показывает, как прошел запрос (200 = ОК, 404 = Не найдено, 500 = Ошибка сервера). 🟢.json() - это магия, которая превращает строку текста от сервера сразу в понятный Python-словарь. 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

☠️ Самая частая ошибка новичков в Python Посмотрите на этот код. Как думаете, что пошло не так? def add_item(item, basket=[])
☠️ Самая частая ошибка новичков в Python Посмотрите на этот код. Как думаете, что пошло не так?

def add_item(item, basket=[]):
    basket.append(item)
    return basket

print(add_item("Яблоко"))
print(add_item("Банан"))

Ожидание: ['Яблоко'] ['Банан'] Реальность: ['Яблоко'] ['Яблоко', 'Банан'] 😱 Почему так произошло? В Python аргументы по умолчанию создаются один раз при запуске программы (когда интерпретатор читает def), а не при каждом вызове функции. Поэтому список basket - это один и тот же объект в памяти для всех вызовов! ✅ Как писать правильно: Используйте None как маркер пустого значения.

def add_item(item, basket=None):
    if basket is None:
        basket = []
    basket.append(item)
    return basket
Сохраняй пост, чтобы не наступать на эти грабли! 💾 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🧩 Задача: Проверка на анаграмму Анаграммы, это слова, состоящие из одних и тех же букв, но в разном порядке. Например: ток и
🧩 Задача: Проверка на анаграмму Анаграммы, это слова, состоящие из одних и тех же букв, но в разном порядке. Например: ток и кот. Как проверить, являются ли две строки анаграммами, максимально эффективно? Вариант 1 (В лоб): Сортировка Мы можем отсортировать обе строки и сравнить их.

def is_anagram(s1, s2):
    return sorted(s1) == sorted(s2)
Это работает, но сложность сортировки - O(N log N) . Можно ли быстрее? 🤔 Вариант 2 (Оптимальный): Подсчет символов Используем словарь (или Counter), чтобы посчитать, сколько раз встречается каждая буква. Сложность такого решения - O(N) , то есть линейная (самая быстрая).

from collections import Counter

def is_anagram_fast(s1, s2):
    return Counter(s1) == Counter(s2)

print(is_anagram_fast("listen", "silent")) # True

💡 Совет: На собеседованиях всегда предлагайте сначала решение с сортировкой (оно проще), а потом удивляйте интервьюера знанием сложности алгоритмов и вариантом с хеш-таблицей (Counter)! 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🕵️‍♂️ Дебаг без боли: секрет f-строк Признавайтесь, вы тоже часто пишете такие принты, чтобы проверить значение переменной?

name = "Alex"
age = 25

print("name =", name)
print("age =", age)

Это работает, но писать каждый раз название переменной в кавычках — долго и лениво. В Python есть супер-фишка для f-строк, о которой многие забывают. Просто добавьте знак равно = после имени переменной внутри фигурных скобок! Как это выглядит:

name = "Alex"
age = 25

print(f"{name=}")
print(f"{age=}")

Вывод:

name='Alex'
age=25

🔥 Почему это круто: Python сам подставляет имя переменной и её значение. Экономит кучу времени при поиске багов! Знали про этот трюк? Ставьте ❤️, если уже пользуетесь! 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🧠 List vs Set: Где искать быстрее? Представьте, у вас есть массив из 1 миллиона чисел, и нужно проверить, есть ли в нем числ
🧠 List vs Set: Где искать быстрее? Представьте, у вас есть массив из 1 миллиона чисел, и нужно проверить, есть ли в нем число 999999. Что использовать: список (list) или множество (set)? Сравнение сложности: ▪️ List (in operator): Сложность . Python перебирает элементы по одному, пока не найдет нужное. Если список огромный, это долго. ▪️ Set (in operator): Сложность (в среднем). Множества реализованы как хеш-таблицы. Python вычисляет хеш числа и сразу "прыгает" в нужную ячейку памяти. Тест скорости:

import time

data_list = list(range(10_000_000))
data_set = set(data_list)

# Поиск в списке
start = time.time()
print(9999999 in data_list)
print(f"List time: {time.time() - start:.5f} sec")

# Поиск в множестве
start = time.time()
print(9999999 in data_set)
print(f"Set time: {time.time() - start:.5f} sec")
🚀 Итог: Если вам нужно часто проверять наличие элемента в большой коллекции - всегда конвертируйте её в set. 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

⚡ Объединяем словари в Python 3.9+ Раньше для объединения двух словарей нам приходилось использовать метод .update() или расп
Объединяем словари в Python 3.9+ Раньше для объединения двух словарей нам приходилось использовать метод .update() или распаковку **kwargs. Начиная с Python 3.9, появился оператор объединения |. Это делает код намного чище! Пример:

x = {"key1": "value1 from x", "key2": "value2 from x"}
y = {"key2": "value2 from y", "key3": "value3 from y"}

# Старый способ (до 3.9)
z = {**x, **y}

# Новый способ (Python 3.9+)
z = x | y

print(z)
# {'key1': 'value1 from x', 'key2': 'value2 from y', 'key3': 'value3 from y'}

💡При совпадении ключей (в примере key2), побеждает значение из последнего словаря (справа от оператора). Сохраняй, чтобы писать чистый код! 💾 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

🐍 Ловушка с аргументами по умолчанию Посмотрите на код ниже. Как вы думаете, что выведет второй вызов функции? def append_to
🐍 Ловушка с аргументами по умолчанию Посмотрите на код ниже. Как вы думаете, что выведет второй вызов функции?

def append_to(num, target=[]):
    target.append(num)
    return target

print(append_to(1))
print(append_to(2))

Варианты ответа: 1. [1] и [2] 2. [1] и [1, 2] 3. Ошибка SyntaxError ⬇️ Правильный ответ и объяснение ⬇️ Правильный ответ: 2 — [1] и [1, 2] Почему? В Python аргументы по умолчанию вычисляются один раз при определении функции, а не при каждом вызове. Список target создается один раз и сохраняет свое состояние между вызовами. ✅ Как исправить? Используйте None в качестве значения по умолчанию:

def append_to(num, target=None):
    if target is None:
        target = []
    target.append(num)
    return target
📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

Path().mkdir() Согласно официальной документации, метод .mkdir() принимает три аргумента. Мы пока сосредоточимся только на pa
Path().mkdir() Согласно официальной документации, метод .mkdir() принимает три аргумента. Мы пока сосредоточимся только на parents и exists_ok. Оба аргумента имеют значение False по умолчанию. Аргумент parents вызывает ошибку FileNotFound в случае отсутствия родителя, тогда как exists_ok вызывает ошибку FileExists, если данный каталог уже существует. 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

Почему при зарплате 400к денег всё равно нет? Замечал, как люди с доходом 80к умудряются копить на отпуск и покупать айфоны? А у многих айтишников при 300-400к — в конце месяца ноль. Знакомо? Есть 5 конкретных причин, почему так происходит при высоком доходе. Снял видео — разбираю каждую. 5 минут, без воды. Посмотри, возможно узнаешь себя или коллег! Смотреть #реклама 16+ sbsite.pro О рекламодателе

Проверяем тип объекта Раз уж в предыдущем посте затронули модуль inspect, то давайте еще поговорим про некоторые его возможно
Проверяем тип объекта Раз уж в предыдущем посте затронули модуль inspect, то давайте еще поговорим про некоторые его возможности. Функции ismodule(), isclass(), ismethod() и isfunction() проверяют переданный объект на то, является ли он модулем, классом, методом или функцией соответственно. 📲 Мы в MAX Подписывайтесь на канал 👉@pythonofff

Свойства Concrete Paths Поскольку Concrete Paths является подклассом PurePath, мы можем использовать все свойства PurePath().
Свойства Concrete Paths Поскольку Concrete Paths является подклассом PurePath, мы можем использовать все свойства PurePath(). Это означает, что мы можем использовать, например, свойство .with_suffix для добавления суффикса к конкретному пути. Подписывайтесь на канал 👉@pythonofff