All streams
Search
Write a publication
Pull to refresh
82
0.7
Tishka17 @Tishka17

Пользователь

Send message

Настройки - не константы. Они гузятся из внешних источников, часто не сразу в момент иницализации кода, а где-то после старта. В тестах часто мы их переопределяем, а некоторые объекты изначально работавшими с одним экземпляром настроек начинают работать с несколькими. Импртирование же в любой модуль всех настроек целиком усложняет потом поиск какие же настройки реально нужны

Всегда удивляло использование @lru_cache как реализацию ленивых глобалов. Я бы сильно подумал и скорее всего вместо таких фокусов взял и воспользовался Depedency Injection (просто при старте передал эти настройки куда надо)

Не смотря на то, что во много я согласен с автором, некоторые выводы мне кажутся странным. Например,

В своих иллюстрациях анкл Боб лукавит — почему‑то в случае плохого дизайна у него в API механизмов фигурируют слова «Keyboard» и «Printer», а в случае хорошего дизайна — они чудесным образом исчезают.

Он не лукавит. В этом суть понятия "абстракция". Поинт абстрагирования не в том чтобы написать abstract перед классом, а в том чтобы скрыть неважные детали. Там где вы предлагаете проектировать API - вы буквально предлагаете заняться абстрагированием.

Выписывание интерфейса вообще не обязано добавить абстракции коду, это вы правильно подметили. Но пробелма как раз в том, что DIP не про слово abstract, а про скрытие деталей.

При этом механические отличия всё ещё остаются. Если вы "спроектировали API", но не положили интерфейс туда где он используется, в следующей версии нет никаких проблем добавить туда публичных методов, завязанных на детали, а потом начать юзать. Выделение интерфейса как раз помогает отделить API от всего остального. Расположение интерфейса рядом с БЛ - помогает следить что мы не используем какие-то детали вроде библиотеки кассандры в этом API.

Да, у этого есть цена, тут нет вопросов.

Прошу прощения, я почему-то думаю по дефолту, что люди придерживаются слоистой архитектуры с выделением соотетствующих абстракций. И тут есть варианты куда у нас направлена связь между БЛ и БД. Если слоев нет, хотелось бы конечно понимать как устроено деление приложения на компоненты.

Репозиторий - это просто один и видов "шлюзов" для доступа к БД. Я решил использовать более широкий термин потому, что не уверен что мы под репозиторием понимаем одно и то же.

Я в рамках данной дискуссии не против возврата модели алхимии, я просто пытаюсь понять кто занимается конвертациями (и сколько их), обычно это задача шлюза для доступа к внешней системе (к БД, например), чтобы бизнес логика занималась своими задачами

Если нам это надо сконвертить в тот тип, который необходим приложению, то это всегда можно сделать внутри сервиса. 

звучит как нарушение изоляции слоев. "Сервис" - это и есть "приложение". Мы для этого и выделяем шлюзы к БД, чтобы дальше работать с удобными типами. Иначе опять же вопрос - а зачем тогда эта "абстракция"?

Имелось ввиду, использовать форматы предназначенные для хранения конфигурации, а не для превращения в переменные окружения. Например, поддерживающих нативно иерархию или хотя бы разделы

В зависимости от проекта требования к работе с настройками могут быть разные. Часто хочется грузить их из конфига, из параметров командной строки, из конфига путь к которому в параметрах командной строке, из переменных окружения, из всего сразу или ещё как. Не для всех приложений это актуально (как я понял - не для вас), но такие требования существуют.

При этом для разных источников настроек мы хотим разное их представление. То, что в переменных окружения лежит как MYAPP_DB_ADDR , в файле будет параметром addr в секции [db] или ещё как.

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

Я не буду вдаваться в крайности и говорить, что pydantic-settings однозначно надо выкинуть (хотя хочется так говорить), но определенно пользоваться надо им очень аккуратно, а преподносится он как готовое решение. Мои рекомендации

  • Под каждый источник данных иметь свой класс PydanticSettings со своей настройкой алиасов и т.п. Это позволит нормально переключакться между ними.

  • В конечных классах использовать просто параметры инита или свои датаклассы. При инстанциировании перекладывать из пидантика в них. Это позволит менять источники данных без изменения бл и адаптеров.

  • Включать case_sensitive и прочие опции

  • Не использовать вложенные сеттинги для тех источников, которые нативно это не умеют.

  • При загрузке энва явно передавать путь.

  • Проектировать формат и структуру конфигурации, а не полагаться на библиотеку

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

Обычно это делает инстурмент запуска. Криминальное тут то, что если уже какой-то инструмент прочитал энвы до того как ты загрузил .env, будет несоответствие. Если так хочется юзать dotenv, то проще настроить запуск программы с помощью команды dotenv: `doetnv python app.py`. Как раз будет вам и .энв и мне переменные окружения заполненные до запуска.

Но в конечном счете, pydantic_settings позволяют тебе довольно гибко загружать нужные переменные, как из окружения, так и из .env файликов.

А вы пробовали настроить выбор откуда грузить не на момент написания кода, а в рантайме? Например, в тестах юзать дефолты и ничего не грузить, если передан параметр --config - грузить по указанному пути и не юзать переменные окружения, а если ничего не указали - грузить .энв и переменные окружения? В общем, любую кастомную логику.

Мое мнение, что если у нас штук 5 переменных окружения - проще прочитать руками. Если сильно больше - имеет смысл подумать над использованием нормального конфига.

Давайте подумаем какие методы у нас доступны в advanced-alchemy? Почти все позволяют указывать непонятные параметры (**kwargs), то есть очень неконкретные. Что же у нас вместо этого есть в алхимии
- session.get() - получить по айди
- session.add() - добавить объект
- session.delete() - удалить объект
Бери и юзай. А если нужны кастомные методы типа list с фильтрами (а кому нужен list без фильтров непонятно) - сделай конкретную обертку над execute.

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

Не надо вызывать dotenv. Переменные окружения должны быть известны к моменту старта приложения, а не модифицироваться внутри него. Они должны прилетать из окружения, а не заполняться приложением самим себе.

Если же мы рассматривает дотэнв просто как конфиг, то имеет смысл взять полноценный формат конфигов, такой как toml/yaml. Там хотя бы иерархия и стандарт есть, в то время как диалекты дотэнва разные, а файл плоский

SQL Alchemy преподносит как минимум три существенных преимущества:

  • Query builder. Это не часть ORM, это просто способ безопасно строить динамический SQL

  • Data Mapper. В отличие от Active Record мы можем маппить на доменные модели. Либо использовать модели алхимии как доменные. Во втором случае сама модель (инстанс), в целом, все ещё просто отображает данные, в БД она сам не пойдет, хотя класс и выглядит грязнее.

  • UoW - мы можем трекать изменения моделей, добавлять в буфер новые чтобы потом разом сохранить.

Первое (query builder) это хорошая фича для реализации слоя работы с БД, в БЛ ей не место. Второе (mapper) позволяет организовывать связь данных в БД и в БЛ. А UoW - это уже часть логики работы с данными, а именно когда они будут попадать в базу данных.

Ни одна из этих крутых фич не требует вам в бизнес логике использовать термины базы данных для построения запросов. Вы все ещё можете сделать `def find(name: str| None, price: int | None)`, а превращение этого в SQL скрыть внутри репозитория, не ломая вышеназванные фичи. А подход с GenericRepo - это как раз та сущность, что ничего толком не дает, а только загрязняет логику - мы переносим часть QueryBuilder в БЛ).

Я бы не был так строг по отношению к моделям, но вот интерфейс метода list максимально неявный, что просто лишает этот репозиторий преимуществ по сравнению с использованием голого session

Вот только pydantic settings делает очень неявные предполжения и может прочитать переменную окружения UsEr вместо USER

Я хочу возразить. В основном гуглю то, что не требует глубины мышления - названия функций, какие-то особенности фреймворков. А глубина мышления как раз есть, она позволяет по двум словам понять о чем речь, как оно может быть устроено и выбрать на том же SO правильный в данном контексте ответ, даже если плюсики говорят другое. Мелкие детали легко находятся и постоянно меняются. А общие принципы и есть та глубина мышления, их сложно нагуглить, но они при этом прослеживаются везде.

Назвать проект venv - это жестоко. Запутать всех решили?

я взял небольшой массив размером 1000000 чисел (это же 4мб памяти, да?) что-то падает, не подскажете почему?

Попробуйте код чуть посложнее - напишите функцию поиска максимума в ФП стиле

Это хорошо, что вы не видели этот паттерн. Я видел и судя по другим комментариям, люди продолжают использовать. IoC-контейнеры же есть не у всех и не у всех скоуп синглтон называется именно так (это классическое название, но все же). Так что когда говорят "синглтон", без уточнений ожидалсь бы, что речь именно о самостоятельной сущности, а не детали реализации какой-то библиотеки (пусть даже такой большой как спринг и аналоги). Второе ожидание, что люди знакомые с IoC-контейнеоами знают и паттерн и способных их отличить. Вот вы справились, а кто-то - нет, и мне кажется виноват не автор

Что же касается книги - возраст не повод её выкидывать. Книга все ещё актуальная и рекомендуется к прочтению всем кто переходит от уровень junior дальше.

Information

Rating
1,859-th
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity

Specialization

Backend Developer, Mobile Application Developer
Lead
Python
Docker
Linux
SQL
Git
Golang
Android SDK