PYTHON IN DEPTH🐍 – Telegram
Гид по множественному присваиванию

Уверена, что множественное присваивание в Python видели все. Выглядит оно вот так:

>>> x, y, z = 1, 2, 3
>>> x
1
>>> z
3

На самом деле на низком уровне создается tuple и в цикле инициализируется значениями 1, 2, 3. Поэтому с тем же успехом можно не опускать скобки и написать вот так:

>>> (x, y, z) = (1, 2, 3)
>>> x
1
>>> z
3

И это тоже сработает. Поэтому множественное присваивание еще называют tuple unpacking. Да и чаще всего его, кажется, используют именно с кортежами. Но вообще так можно писать с любыми iterable типами:

>>> x, y = [1, 2]
>>> y
2

И со строками тоже:

>>> x, y = 'hi'
>>> x
'h'
>>> y
'i'

Множественное присваивание, если применять его с оператором *, позволяет делать еще вот такие крутые штуки:

>>> numbers = [1, 2, 3, 4, 5, 6]
>>> first, *rest = numbers
>>> first
1
>>> rest
[2, 3, 4, 5, 6]

По сути * здесь заменяет слайсы. С тем же успехом можно было написать:

>>> first, last = numbers[0], numbers[1:]
>>> first
1
>>> last
[2, 3, 4, 5, 6]

Но через распаковку получается читаемее. Можно даже <<вырезать>> середину:

>>> first, *middle, last = numbers

И если вам не нужны все промежуточные индексы, то хороший тон это вообще использовать нижнее подчеркивание:

>>> first, *_, last = [1, 2, 3, 4, 5, 6]

Кстати, если речь о вложенных объектах, то обратите внимание, что копия будет глубокой:

>>> color, point = ('blue', (0, 0, 255))
>>> color
'blue'
>>> point
(0, 0, 255)

В некоторых случаях это очень удобно.

В общем, множественное присваивание можно использовать не только с кортежами, но и списками, строками и любыми iterable типами. Его плюс в том, что оно позволяет не использовать индексы, а значит, уменьшает склонность к ошибкам и делает код читаемее. А еще оно используется в <<args, kwargs>> синтаксисе, который часто смущает начинающих, но об этом в следующий раз. 🐠
👍7🤔1
Введение в декораторы

В нескольких следующих постах я хочу поговорить о декораторах. Будет базовое определение, мотивация их использовать, всякие хитрости, а еще куча примеров.

Прежде, чем говорить о декораторах, нужно кое- что узнать о функциях в Python. Допустим, у нас есть функция, которая здоровается с Юпи:

def hey_Jupi():
print("Привет, Юпи!")

Функции в Python -- это объекты первого класса, ничем не хуже, чем int'ы или словари. Это значит, что:

🐙 Функцию можно присвоить переменной:

say_hi = hey_Jupi
say_hi()
# Привет, Юпи!

🐙 Функцию можно вернуть из функции:

def wrapper(func):
print("Юпи пришла.")
return func

hello_Jupi = wrapper(hey_Jupi)
# Юпи пришла.
hello_Jupi()
# Привет, Юпи!

🐙 Функцию можно определить внутри другой функции:

def deco(func):
def wrapper():
print("Юпи пришла.")
func()
return wrapper

hey_Jupi = deco(hey_Jupi)
hey_Jupi()
# Юпи пришла.
# Привет, Юпи!

Смотрите, что получилось на последнем шаге. На этапе создания deco никакой код не выполняется -- мы заходим в deco, видим, что здесь определена функция wrapper и возвращаем ее. Таким образом мы подменяем исходную hey_Jupi на wrapper и получаем новое поведение hey_Jupi, не изменяя ее код!

Это и называется декоратор. Это настолько удобный и мощный инструмент, что в Python для него придумали специальный синтаксический сахар. При условии, что функция deco у нас уже определена так же, как выше, можно добавить название декоратора с символом @ перед определением функции и получить эквивалентное поведение:

@deco
def hey_Jupi():
print("Привет, Юпи!")

hey_Jupi()
# Юпи пришла.
# Привет, Юпи!

Кстати, этот же декоратор можно применить и к любой другой функции:

@deco
def take_five():
print("Юпи, дай пять!")

take_five()
# Юпи пришла.
# Юпи, дай пять!

Декораторы круты тем, что позволяют гибко модифицировать поведение функции, применять одну и ту же модификацию к нескольким функциям сразу и даже менять поведение функций, доступа к коду которых у нас нет! Зачем нам декораторы на реальных проектах?

🐙 Декораторы используют в веб фреймворках для проверки авторизации или для разделения групп пользователей. Например, часть методов доступна только авторизованным пользователям, либо пользователям с определенной ролью, а остальные методы -- всем. Для этого нужные методы оборачивают в декораторы, которые делают необходимые проверки.
🐙 Декораторы позволяют проверить, что аргументы функции имеют нужный тип и значения. Это можно сделать на входе в функцию, но иногда проверки переносят в функцию-обертку.
🐙 С помощью декоратора можно замерять время выполнения функций.

В следующих постах разберемся, как комбинировать декораторы и передавать в декоратор параметры. Всем пять!🐠
👍6🔥1
Комбинируем декораторы

Допустим, у нас есть функции, которые работают со строками. Возьмем, например, функцию, которая возвращает строчку из Zen of Python:

def readability():
return 'Readability counts'

print(readability())
Readability counts

А теперь мы хотим, чтобы можно было показать текст в двух вариантах: жирным и курсивом. Вот два декоратора, один из которых добавляет тег <<жирный>>, а второй <<курсив>> :

def bold(func):
def wrapper():
return '<b>' + func() + '</b>'
return wrapper

def italic(func):
def wrapper():
return '<i>' + func() + '</i>'
return wrapper

Как уже договаривались в прошлом посте запись:

@bold
def readable():
return 'Readability counts'

Эквивалентна записи

readable = bold(readable)

И после применения декоратора вызов функции дает текст

print(readable())
<b>Readability counts</b>

А что если мы хотим получить текст одновременно и жирный и с курсивом? Можно сделать композицию декораторов:

@italic
@bold
def readable():
return 'Readability counts'

print(readable())
<i><b>Readability counts</b></i>

Обратите внимание, что результат в зависимости от порядка применения декораторов будет разным:

@bold
@italic
def readable():
return 'Readability counts'

print(readable())
<b><i>Readability counts</i></b>

Так получается из-за разного порядка выполнения функций: в первом случае мы сначала добавляем тэг <<жирный>>, а сверху оборачиваем в <<курсив>>:

readable = italic(bold(readable))

А во втором -- наоборот:

readable = bold(italic(readable))
👍3🔥3
Исключения
Представьте, вы приступаете к работе над новым проектом и обнаруживаете в логах следующие сообщения, которые смешаны с успешными запросами к серверу:

Unexpected exception:

Что-то постоянно ломается, но без каких-либо сообщений.

Если сократить проблему до минимального примера из кода приложения, с которым я недавно столкнулся, получится следующая конструкция, которая на первый взгляд не вызывает подозрений:

try:
raise KeyError
except Exception as e:
print(f"Unexpected exception: {e}")

При выполнении этого примера получим следующий результат:

Unexpected exception:

Проблема заключается в том, что есть четыре способа вывести сообщение об исключении:

🐟 print(e)
🐟 print(str(e))
🐟 print(e.message)
🐟 print(repr(e))

Первые два варианта не особенно информативны. Например, если мы попытаемся обратиться к несуществующему ключу в словаре (самая распространенная ошибка в Python), то эти варианты выведут только название ключа.

my_dict = {}
try:
b = my_dict["bad"]
except Exception as e:
print(f"Unexpected exception: {e}")

Unexpected exception: 'bad'

Это происходит потому, что str(e) и e выводят только сообщение об ошибке, но не ее тип. Чаще всего этого достаточно, но исключения существуют для того, чтобы информировать о ситуациях, которые не были ожидаемы.

Иногда люди используют print(e.message). Однако здесь возникают две проблемы: во-первых, мы все равно получаем только сообщение об ошибке, а не ее тип. Во-вторых, атрибут message не определен для всех исключений. Если мы не проверим наличие этого атрибута перед использованием, мы получим новое исключение:

AttributeError: 'KeyError' object has no attribute 'message'

Однако магический метод repr, который используется для предоставления наиболее точного описания, справляется с задачей. Рассмотрим сравнение:

try:
raise KeyError
except Exception as e:
print(f"Unexpected exception: {repr(e)}")

Unexpected exception: KeyError()

В случае с использованием словаря получим:

Unexpected exception: KeyError('bad')

что является более явным, чем все три предыдущих варианта
2🔥2
Еще про исключения:

repr(e) -- это, конечно хорошо, но ведь есть ещё лучше, а именно:

from traceback import print_exc
...
my_dict = {}
try:
b = my_dict["bad"]
except Exception as e:
print_exc()

что выведет:

Traceback (most recent call last):
File "<pyshell#1>", line 2, in <module>
KeyError: 'bad'

и при этом программа продолжит свою работу. А если ошибку надо выводить не в stdout, то можно сделать так:

from traceback import print_exc
from io import StringIO
...
try:
b = my_dict["bad"]
except Exception:
buffer = StringIO()
print_exc(file=buffer)
out_var = buffer.getvalue()
🔥3👍2
Дублирование объектов в множестве

Предположим
, у нас есть сервер и его клиенты. Мы хотим отслеживать состояние клиентов на сервере и управлять ими. Для этой цели мы будем добавлять клиентов в коллекцию, чтобы избежать случайного дублирования одного и того же клиента на сервере.

Давайте создадим класс Client и добавим его в коллекцию (я также добавил метод repr для красивого вывода):

class Client:
def init(self, user_name):
self.user_name = user_name

def repr(self):
return self.user_name

fish1 = Client(user_name="catfish")

clients = set()
clients.add(fish1)

print(clients) # {catfish}

Замечательно, всё работает как задумано! Теперь попробуем добавить второго клиента и убедимся, что дублирования не происходит:

fish2 = Client(user_name="catfish")
clients.add(fish2)

print(clients) # {catfish}

Как это возможно? Мы добавили два абсолютно идентичных экземпляра в коллекцию, ожидая, что останется только один, но оба остались.

При добавлении объекта в коллекцию интерпретатор следует следующему правилу:

если a == b, то hash(a) == hash(b) должно быть обязательно выполнено.

То есть, интерпретатор сравнивает объекты не только напрямую, но также сравнивает их хеши.

Для сравнения объектов, коллекции и словари используют магический метод eq. Для пользовательских объектов этот метод определен по умолчанию. По умолчанию, два разных объекта не считаются равными, даже если они абсолютно идентичны:

print(fish1 == fish2) # False

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

🦑 В Python 2.6 и более ранних версиях hash(x) = id()
🦑 В Python 2.6 и более поздних версиях: (https://bugs.python.org/issue5169) hash(x) == id(x)/16

То есть, нельзя полагаться на то, что hash(x) = id().

Однако можно полагаться на то, что в Python у объектов по умолчанию есть хеш-значение, которое зависит от их идентификатора (id), и что хеш объекта не меняется в течение его жизни.

Так как в нашем примере объекты разные и занимают разные ячейки памяти, мы получаем:

print(hash(fish1), hash(fish2)) # 8786876890805 8786876904409

Так что можно сделать, чтобы объекты, которые мы считаем одинаковыми, объединялись в коллекции? Нужно переопределить методы hash и eq, чтобы явно указать интерпретатору, как сравнивать объекты и как рассчитывать хеш.

Для этого внесем изменения в определение класса:

class Client:
def init(self, user_name):
self.user_name = user_name

def repr(self):
return self.user_name

def hash(self):
return hash(self.user_name)

def eq(self, other):
if self.user_name == other.user_name:
return True
else:
return False

Теперь, при добавлении объектов в коллекцию, всё работает так, как ожидалось:

clients = set()

fish1 = Client(user_name="catfish")
clients.add(fish1)
fish2 = Client(user_name="catfish")
clients.add(fish2)

print(clients) # {catfish}

Помните, чтобы добавить объект вашего собственного класса в коллекцию или словарь в Python, вам необходимо:

🦑 Переопределить и метод hash, и метод eq, иначе это не будет работать (https://docs.python.org/3/reference/datamodel.html#object.__hash__)
🦑 Если объекты идентичны, их хеши должны быть равными.
🦑 Хеш-функция должна содержать информацию, которая однозначно идентифицирует объект.
🦑 Хеш объекта не должен изменяться в течение его жизни (иначе могут возникнуть проблемы во время выполнения программы).
👍51
🖥Microsoft интегрировала Python в Excel

Все популярные библиотеки Python (pandas, statsmodels и matplotlib) стали доступны для создания графиков и диаграмм. Для реализации всего этого появилась новая функция "PY"
Please open Telegram to view this post
VIEW IN TELEGRAM
7🔥3
Топ популярных горячих клавиш, для PyCharm:

Ctrl + D
— Дублировать строку, когда пишешь схожие строки, теперь нет надобности набирать их сначала или выделять и копировать.
Ctrl + R — Решил переименовать класс? Изменит имя во всем проекте.
Ctrl + Shift + N — Поиск класса или метода по всему проекту.
Ctrl + Alt + M — Написали код, теперь захотели его обернуть в функцию, вот сочетание.
Ctrl + Alt + S — Перейти в настройки.
Ctrl + Y — Удалить строку.
Ctrl + B — Переместиться к данному классу.
Ctrl + F12 — Показывает структуру данных файла.
Alt + F7 — Посмотреть где используется данный класс, метод или функция.
Ctrl + Shift + U — Быстро изменить регистр слов.
Ctrl + Alt + L — Быстрое форматирование кода по стандарту PEP 8.
Ctrl + Shift + ↑ ↓ — Для быстрого перемещения строк или блоков.
Ctrl + W — Выделить текущий блок.
9👍7🔥2
Forwarded from Хитрый Питон
Вышла первая бета Django 5.0, а значит значимого изменения состава релиза уже не будет и можно смотреть, что завезли:

1. Много поддержки асинхронности - в contrib.auth, возможность получить и обработать asyncio.CancelledError внутри вьюхи, если клиент разорвал соединение до того, как мы закончили обрабатывать запрос, поддержка асинхронки в куче декораторов, асинхронная отправка сигналов, новые асинхронные методы у моделек
2. На первом месте довольно спорная фича - возможность показывать количество фильтруемых объектов в боковом фильтре в админке. Там под капотом COUNT и естественно на более-менее приличных объемах данных тормозить будет нечеловечески. Благо можно глобально отключить
3. Упрощенная шаблонизация для форм из коробки, для тех кто работает с html-формами код станет читабельнее (хотя думается мне, что те кто работает с большими формами уже давно что-то подобное у себя реализовали)
4. Возможность задавать дефолты на уровне базы данных
5. `GENERATED`-поля в моделях, значение которых рассчитывается на уровне БД
6. В Choice-полях теперь можно использовать словарь, вместо кортежей

В общим никаких революций https://docs.djangoproject.com/en/5.0/releases/5.0/
🔥31👍1🫡1
Forwarded from Хитрый Питон
Новости nogil. Steering Council принял PEP 703 - опциональный gil из коробки. На discuss.python.org большой пост про это, я приведу только заинтересовавшие меня моменты:

Пока нет уверенности, что выпиливание gil не поломает совместимость со сторонними либами, не сделает интерпретатор медленнее на 10–15% и не усложнит его поддержку. Выбрали вполне ожидаемый подход - реализуем и посмотрим, как пойдет, потому что на прототипах и теоретических выкладках далеко не уедешь.

Как и предполагали раньше, все будет происходить в несколько этапов:

1. Возможность отключить gil на этапе сборки — это точно не продакшен решение, а инструмент для авторов библиотек и экспериментаторов
2. Финализация изменений API и ABI, после чего nogil-сборка будет уже считаться не экспериментальной но все еще не включаться по дефолту
3. Отключенный gil по умолчанию с возможностью включить при сборке

В общем планируют переходить очень постепенно и не загонять людей в светлое будущее палкой вплоть до того, что готовы отозвать PEP 703 если переход окажется слишком калечащим.

https://discuss.python.org/t/pep-703-making-the-global-interpreter-lock-optional-in-cpython-acceptance/37075
👍32🔥1
Forwarded from Витя пишет код (Victor Luferov)
Еееееее, а че мы над js смеемся
👍2😱2
Интеграции с внешними системами: как не превратить backend в зоопарк адаптеров

Рано или поздно backend обрастает интеграциями: платежи, CRM, учётные системы, доставка. Если не договориться с собой о правилах — код быстро начинает жить по законам чужих API.

Несколько вещей, которые реально помогают не потерять контроль над архитектурой.

1. Anti-corruption layer

Не смешивайте доменные модели с ответами партнёров. Лучше выделить отдельный слой.

class PaymentGateway:
async def create_payment(self, dto: PaymentDTO) -> Payment:
...


Контракт у партнёра изменился — правки остаются в одном месте.

2. DTO как точка входа

DTO — удобное место для валидации и нормализации данных.

@dataclass
class PartnerOrderDTO:
external_id: str
total: Decimal


На этом этапе проще отфильтровать неконсистентные данные, чем ловить баги дальше в домене.

3. Idempotency

Повторы запросов — нормальная история для любой сети.
Idempotency-key или уникальный индекс часто спасают от дублей и лишних списаний.

4. Retry без фанатизма

Бесконечные retry под нагрузкой легко добивают зависимый сервис — а заодно и ваш.
Обычно хватает ограниченного числа попыток с backoff.

Чем меньше домен знает о чужих форматах, тем спокойнее переживаются изменения во внешних системах.
2🔥1😁1
Ваш backend слишком сложный. Скорее всего — без причины.

У большинства команд сложность растёт незаметно.
Сначала “давайте сразу сделаем правильно”, потом появляются абстракции, фабрики, менеджеры менеджеров — и через полгода никто не понимает, как это работает.

Плохая новость: сложность почти всегда дороже железа.

Несколько тревожных сигналов:

— Новый разработчик боится трогать код
— Любое изменение требует правок в 5 слоях
— Архитектуру нужно объяснять на созвоне
— Без схемы в Miro ничего не ясно

Частая причина — преждевременное масштабирование. Система ещё не испытывает нагрузку, а её уже проектируют как Netflix.

Что обычно работает лучше:

1. Простые сервисы
Если класс нельзя объяснить за 30 секунд — он почти наверняка перегружен.

2. Меньше “универсальных” решений
Код, который “подойдёт на будущее”, часто не нужен вообще.

3. Оптимизация по фактам
Сначала метрики → потом усложнение. Не наоборот.

4. Локальная понятность
Открыл файл — понял, что происходит. Это underrated свойство хорошего backend-а.

Интересный парадокс:
сильные инженеры чаще упрощают систему, а не усложняют её.

Иногда лучший архитектурный рефакторинг — это удаление половины кода.
1👍1🔥1
Как на самом деле работает отбор резюме в 2026 году

Современный найм — это не «человек читает ваше резюме».
Это нейронайм: автофильтры, парсеры и быстрые решения рекрутеров.
Большинство кандидатов отсеиваются до того, как их резюме кто-то осознанно посмотрит.

Базовые фильтры, о которых не говорят вслух
- Опыт менее 3 лет в большинстве случаев автоматически ведёт к отказу.
- Возраст ниже 25 лет часто не проходит первичную фильтрацию, даже если это нигде не написано.
- Отсутствие ключевых слов в блоке навыков = отказ.
Автопарсеры сопоставляют резюме с вакансией по совпадению терминов, а не по смыслу.

Учебные практики и стажировки не считаются коммерческим опытом.
Неайтишный бэкграунд не усиливает резюме айтишника — он его размывает.

Что не стоит указывать в резюме
Желаемый доход
Личные соцсети
Фотографию

🖥GitHub не всегда важен и редко смотрится на этапе фильтрации.
Образование в большинстве случаев вообще не проверяется.

Зато важно:
👉 указывать стек технологий отдельно для каждой компании, а не общим списком.

Резюме — это продажа, а не автобиография


Успешный программист умеет продавать себя.
Продажи в найме бывают:
- тупыми — «я хороший, возьмите меня»
- умными — «я закрываю вашу боль»

На HeadHunter обязательно используйте инструменты, которые повышают ваш статус в выдаче.
Это напрямую влияет на количество просмотров.

Не тратьте время на «подтверждение навыков».
Лучше используйте репозиторий на GitHub как опору, а не как главный аргумент.

Умная продажа — это:
- правильное позиционирование,
- попадание в запрос работодателя,
- демонстрация пользы, а не личности.

Как должно выглядеть сильное резюме

Резюме — это рекламный буклет, а не отражение вашей души.
Через него нужно донести:
- умение общаться,
- способность решать задачи,
- ответственность за результат.

Каждое место работы описывается по одному шаблону:

Контекст — где и в каких условиях вы работали
Достижения — что изменилось благодаря вам
Стек — конкретные технологии

Достижения оформляются по модели SART:
Situation — ситуация
Aim — цель
Result — результат
Tools — инструменты


Метрики, цифры и проценты крайне важны, даже если работодатель не разбирается в разработке.
Проактивность и инициативность повышают вес кандидата сильнее, чем ещё один фреймворк.

Блок «О себе»: как делать правильно

Он должен быть структурированным:
- Короткое интро
- Ваша профессиональная философия
- Текущий стек
- Хобби (уместно)
- Контакты

Контакты — актуальные и профессиональные.
Никаких личных телег и мем-юзернеймов.

Плагиат — это нормально (если с умом)

Плагиат допустим, если вы можете объяснить текст.
Адаптация чужого сильного резюме под свои знания и опыт — хорошая стратегия, а не грех.

Про вакансии и отклики

Единственный реально важный фильтр — язык, указанный в названии вакансии.
Остальные фильтры дают шум и искажения.

Важно:
- внимательно читать описание вакансии,
- смотреть на технологии, которые реально используются в компании.

Резюме без ключевых слов или несоответствующее возрасту и опыту почти не имеет шансов.

Не тратьте время на вдумчивый просмотр вакансий.
Фокус — на массовой рассылке.

Нормальный рабочий показатель — 150–200 откликов в день.

Сопроводительные письма

Многие компании их требуют, но:

не пишите вручную

используйте копипаст с адаптацией

Избегайте клише вроде:

«Здравствуйте, меня зовут Иван, у меня 4 года опыта…»

Пишите коротко, по делу и по контексту вакансии.


Сделайте эти простые шаги и может быть вас заметят, а может и нет.
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1💩1