Ваша основная деятельность:
Anonymous Poll
24%
data analysis / data science
20%
веб разработка (django / flask / sanic / fastapi / etc)
9%
тестировка
33%
другое
36%
я слива лиловая, спелая, садовая...
Дрочим датаклассы v 2.0 💅💅
Приветствую, дорогие читатели! Ваш любимый шизоид снова выходит на связь. В этом посте мы закончим разбирать датаклассы (надеюсь). В прошлом посте я не осветил такой интересный вопрос, как "зачем же все-таки они нужны?". Поясняю: вам необходимо создавать датаклассы в первую очередь для хранения и управления данными (как и следует из их названия). Либо, если вы хотите упростить себе задачу и писать меньше кода, воспользовавшись автоматической генерацией некоторых методов:
У нас в проектах это много где используется, в основном для упрощения управления и хранения различного рода данных.
Исключение аргументов из
Рассмотрим пример, когда нам нужно убрать аргумент из инициализатора, например
Используем
Иногда нам необходимо например добавить переменную, которая будет являться комбинацией других. Для этого воспользуемся методом
Выведет:
Private/protected
Абсолютно аналогично созданию private/protected переменной в обычном классе. Просто добавляем _ перед именем.
Read only поля датакласса
Допустим мы хотим сделать так, чтобы после инициализации значения полей нельзя было поменять. По умолчанию, когда мы используем декоратор
Теперь после инициализации мы не сможем изменить значения полей:
Указывая этот аргумент мы можем вынудить программиста, использующего наш датакласс, использовать только именованые аргументы при инициализации.
Ну че, все готовы к началу рабочей недели? 🗿🗿🗿
@hw_code
Приветствую, дорогие читатели! Ваш любимый шизоид снова выходит на связь. В этом посте мы закончим разбирать датаклассы (надеюсь). В прошлом посте я не осветил такой интересный вопрос, как "зачем же все-таки они нужны?". Поясняю: вам необходимо создавать датаклассы в первую очередь для хранения и управления данными (как и следует из их названия). Либо, если вы хотите упростить себе задачу и писать меньше кода, воспользовавшись автоматической генерацией некоторых методов:
__init__
, __repr__
, etc.У нас в проектах это много где используется, в основном для упрощения управления и хранения различного рода данных.
Исключение аргументов из
__init__
Рассмотрим пример, когда нам нужно убрать аргумент из инициализатора, например
user_id
, который нам должен генерироваться автоматически. Для этого в аргумент добавим init=False
.
import random
def my_super_ultra_id_func() -> int:
return random.randint(0, 1000)
@dataclass
class User:
user_id: int = field(init=False, default_factory=my_super_ultra_id_func)
name: str = field(default="")
is_active: bool = True
email_addresses: list[str] = field(default_factory=list)
Используем
__post_init__
для создания полейИногда нам необходимо например добавить переменную, которая будет являться комбинацией других. Для этого воспользуемся методом
__post_init__
.
@dataclass
class User:
user_id: int = field(default_factory=my_super_ultra_id_func)
name: str = field(default="")
is_active: bool = True
email_addresses: list[str] = field(default_factory=list, repr=False)
search_string: str = field(init=False)
def __post_init__(self) -> None:
self.search_string = f"{self.user_id}: {self.name}"
user = User(user_id=1, name="hw_code", is_active=False, email_addresses=["[email protected]"])
Выведет:
User(user_id=1, name='hw_code', is_active=False, search_string='1: hw_code')
Private/protected
Абсолютно аналогично созданию private/protected переменной в обычном классе. Просто добавляем _ перед именем.
_search_string: str = field(init=False)
Read only поля датакласса
Допустим мы хотим сделать так, чтобы после инициализации значения полей нельзя было поменять. По умолчанию, когда мы используем декоратор
@dataclass
, значение аргумента frozen
в нем равно False
. Чтобы сделать "заморозить" поля датакласса, меняем это значение на True
.
@dataclass(frozen=True)
class User:
user_id: int = field(default_factory=my_super_ultra_id_func)
name: str = field(default="")
is_active: bool = True
email_addresses: list[str] = field(default_factory=list, repr=False)
search_string: str = field(init=False)
def __post_init__(self) -> None:
self.search_string = f"{self.user_id}: {self.name}"
Теперь после инициализации мы не сможем изменить значения полей:
user = User(user_id=1, name="hw_code", is_active=False, email_addresses=["[email protected]"])
user.name = "Big Dick" # ошибка
kw_only
аргументУказывая этот аргумент мы можем вынудить программиста, использующего наш датакласс, использовать только именованые аргументы при инициализации.
@dataclass(kw_only=True)
class User:
user_id: int = field(default_factory=my_super_ultra_id_func)
name: str = field(default="")
is_active: bool = True
email_addresses: list[str] = field(default_factory=list, repr=False)
search_string: str = field(init=False)
def __post_init__(self) -> None:
self.search_string = f"{self.user_id}: {self.name}"
# сработает
user = User(user_id=1, name="hw_code", is_active=False, email_addresses=["[email protected]"])
# не сработает
user = User(1, "hw_code", False, ["[email protected]"])
Ну че, все готовы к началу рабочей недели? 🗿🗿🗿
@hw_code
Ну и напоследок еще один немаловажный момент, связанный с датаклассами. Иногда нам в таком классе нужна чисто классовая переменная (сори за масло масляное, cls var). Рассмотрим на примере с датаклассами и без.
Создание переменной класса
Без датакласса мы бы написали так
Используя датакласс мы бы написали так
В последнем примере вернул поля и для
Здесь в принципе без детальных пояснений. С
@hw_code
Создание переменной класса
Без датакласса мы бы написали так
class User:
cvar: float = 0.5
def __init__(self, user_id: int, name: str) -> None:
self.user_id = user_id
self.name = name
Используя датакласс мы бы написали так
from typing import ClassVar
@dataclass
class User:
cvar: ClassVar[float] = 0.5
user_id: int = field(default_factory=my_super_ultra_id_func)
name: str = field(default="")
В последнем примере вернул поля и для
user_id
и для name
, поскольку именованый cvar
должен был бы по идее идти после них, но я оставил его в начале.Здесь в принципе без детальных пояснений. С
typing
наверняка все знакомы, если на норм проектах пишете... Как я и говорил, датаклассы абузят понятие классовой переменной, и базовый синтаксис в классах, но зато взамен вы получаете множество уже реализованных "под капотом" методов для работы с данными. В целом, справедливый обмен. Если остались какие то вопросы, в доке все довольно хорошо объяснено, и всегда можно заглянуть туда.@hw_code
Хотел чет еще вкинуть по логгированию или юнит тестам, но увы не успел. Итоги подводить не буду, сами для себя думаю уже все это сделали. Так что, без лишний предисловий, с нг. 🥳
🚀 Привет! Давненько не заглядывал к вам сюда, дел было много, как всегда. Недавно наткнулся в твиттере на обсуждение Groq. Парень там хвалит эту штуку, говорит, что у него время респонса офигенное, и в целом не уступает даже ChatGPT. А я решил спроектировать клиент для использования их API, и самое крутое – без необходимости закрытого ключа!
Хотя pylint, правда, решил подкинуть мне немного головной боли и отказывается работать. Видимо, я мог пропустить мелкий косяк где-то. Хотя вроде бы все работает без нареканий.
Если интересно, вот ссылка на мой проект: GitHub - Dartrisen/groq
Хотя pylint, правда, решил подкинуть мне немного головной боли и отказывается работать. Видимо, я мог пропустить мелкий косяк где-то. Хотя вроде бы все работает без нареканий.
Если интересно, вот ссылка на мой проект: GitHub - Dartrisen/groq
GitHub
GitHub - Dartrisen/groq: groq client
groq client. Contribute to Dartrisen/groq development by creating an account on GitHub.
Наконец то норм фронтэнд на питоне? А мне только недавно зашел Next.js...
https://x.com/jeremyphoward/status/1818036923304456492
https://fastht.ml
Надо попробовать на досуге.
https://x.com/jeremyphoward/status/1818036923304456492
https://fastht.ml
Надо попробовать на досуге.
fastht.ml
FastHTML
Modern web applications in pure Python
Вот бывает такая фигня, работаешь в своем бранче, никого не трогаешь, но тут надо тесты писать. Написал, запускаешь nox, показывает что coverage 99% где то в совсем другой части проекта, никак не связанной с твоим кодом. Начинаешь вопрошать мол как оно попало в master? Стягиваешь master, запускаешь nox и показывает зеленый билд... Меняешь бранч, показывает что проблема все таки есть. Снова в master обратно, и уже и там показывает что все плохо. Но ведь это все уже в мастере, вот как так? А github actions который у нас там подвязан в CI/CD тоже показывает зеленый билд 🥲
Я эти тесты на одном месте крутил. Особенно если их писал не я. Это конечно не часто случается, но когда проект содержит десятки тыщ строк кода, может случиться и такое.
Я эти тесты на одном месте крутил. Особенно если их писал не я. Это конечно не часто случается, но когда проект содержит десятки тыщ строк кода, может случиться и такое.
В каком то из давних интервью меня попросили предсказать вывод следующего Python-кода:
Эту проблему на тот момент я не мог решить, но я ее запомнил и вернулся к ней только недавно. Ключ к решению этой задачи лежит в понимании т.н. Method Resolution Order (MRO) в Python. MRO определяет порядок, в котором происходит поиск базовых классов при обращении к методу или атрибуту.
💡 В данном случае
Вот объяснение:
1️⃣ Python ищет
2️⃣ Затем он смотрит в
3️⃣ Далее он проверяет
Поскольку
Вы можете увидеть MRO с помощью
@hw_code
class A:
pass
class B:
value = 1
class C:
value = 3
class D(A, B, C):
def __str__(self):
return str(self.value)
print(D())
Эту проблему на тот момент я не мог решить, но я ее запомнил и вернулся к ней только недавно. Ключ к решению этой задачи лежит в понимании т.н. Method Resolution Order (MRO) в Python. MRO определяет порядок, в котором происходит поиск базовых классов при обращении к методу или атрибуту.
💡 В данном случае
D
наследуется от A
, B
и C
. Но когда мы печатаем D()
, атрибут value
берётся из класса B
! Почему?Вот объяснение:
1️⃣ Python ищет
value
в классе D
.2️⃣ Затем он смотрит в
A
, но A
не имеет value
.3️⃣ Далее он проверяет
B
— и находит value = 1
.Поскольку
B
стоит перед C
в MRO, используется value
из B
, поэтому вывод будет 1
! ⚙️Вы можете увидеть MRO с помощью
D.mro()
:
[<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
@hw_code
Hello World
Вот бывает такая фигня, работаешь в своем бранче, никого не трогаешь, но тут надо тесты писать. Написал, запускаешь nox, показывает что coverage 99% где то в совсем другой части проекта, никак не связанной с твоим кодом. Начинаешь вопрошать мол как оно попало…
каким-то мистическим образом проблема разрешилась... Вот есть что-то такое в этой зелёной галочке эээ успокаивающее?
Я в последнее время часто сижу в Твиттере. Там собралась большая техническая тусовка: люди постоянно создают новые проекты и стартапы, зарабатывают деньги и делятся опытом. После выхода того самого интервью с Лексом Фридманом и Питером Левелсом (ну про Фридмана-то вы наверняка слышали?), там активно обсуждают, нужно ли действительно "вылизывать" свои приложения, делать тесты и писать код "как надо", а не как хочется. Питер высказывался о том, что все эти TDD, Vercel, крутые базы данных и прочее — не так уж и важны. Он буквально на коленке создал вирусные AI-проекты, которые приносят огромные деньги. Инди-хакеры тут же подхватили эту идею и начали постить, мол, да, надо пушить в продакшен сразу, тесты не нужны, вообще всё не нужно — хочу и пушу говнокод прямо в мастер. "Что вы мне сделаете?" Поэтому мне интересно, что вы думаете по этому поводу:
"Правильных" ответов не существует :)
Anonymous Poll
44%
пушим в прод, 0 тестов, ту зэ мун $1kk ARR (бабки)
56%
тесты, постгрес везде, фабрички Clean Architecture (хороший код)
Мда, если даже здесь всё не так однозначно... Ладно, вот вам ещё один спорный тезис. Кто-то в обсуждениях заметил, что некоторые программерские инструменты будто бы специально сделаны сложными в использовании. Например, те же СУБД (почему-то не люблю это слово). Postgres — это, конечно, мощно, но если у вас тоже возникал вопрос: почему бы не использовать MySQL или даже sqlite, то вы не одиноки. На примере того же Левелса можно отметить, что его сервера выдерживают большой наплыв пользователей, несмотря на использование самых простых инструментов, таких как sqlite. Я, честно говоря, вообще не уверен знает ли он, что есть асинхронная обработка реквестов и т.д. (интересно, как оно на пыхе там?).
Можно долго спорить, но у каждого инструмента есть свои причины для использования. Если ваш проект используют всего трое человек (вы и ещё два фаундера), возможно, не стоит стремиться "сделать всё идеально с самого начала". Но это больше про предпринимательство. Пока нет чёткого видения product market fit, нет смысла усложнять процесс разработки. Если нужно будет, перепишите потом. Одно дело — работать в корпорации, где стабильно платят и цена ошибки не так велика (я знаю примеры, когда тулзы разрабатывали буквально годами, чтобы в итоге сказать "всё, мы решили забросить этот проект"), и совсем другое — когда денег нет и нет чёткого понимания рынка (нужно разработать быстро).
Можно долго спорить, но у каждого инструмента есть свои причины для использования. Если ваш проект используют всего трое человек (вы и ещё два фаундера), возможно, не стоит стремиться "сделать всё идеально с самого начала". Но это больше про предпринимательство. Пока нет чёткого видения product market fit, нет смысла усложнять процесс разработки. Если нужно будет, перепишите потом. Одно дело — работать в корпорации, где стабильно платят и цена ошибки не так велика (я знаю примеры, когда тулзы разрабатывали буквально годами, чтобы в итоге сказать "всё, мы решили забросить этот проект"), и совсем другое — когда денег нет и нет чёткого понимания рынка (нужно разработать быстро).
Hello World
Мда, если даже здесь всё не так однозначно... Ладно, вот вам ещё один спорный тезис. Кто-то в обсуждениях заметил, что некоторые программерские инструменты будто бы специально сделаны сложными в использовании. Например, те же СУБД (почему-то не люблю это слово).…
не, ну фабричка то пусть там все равно пыхтит туда сюда... если надо, на Rust перепишем 👍