Максим @danilovmy
Программист разработчик
Информация
- В рейтинге
- 1 979-й
- Откуда
- Zams, Tirol, Австрия
- Дата рождения
- Зарегистрирован
- Активность
Специализация
Backend Developer, Fullstack Developer
Lead
От 7 000 €
Python
Django
Ajax
OOP
Design patterns
Vue.js
JavaScript
HTML
CSS
Цитируя документацию, ссылку на которую я привел в первом ответе:
Инициализация объекта происходит непосредственно перед вызовом beforeCreate (Called immediately when the instance is initialized and props are resolved)
Это действительно может смущать, что есть какие-то переменные, которые объявляются до появления instance, и они станут атрибутами this после. Мы даже можем управлять видимостью этих переменных и решать, какие переменные в setup(), будут после отображены в instance, как атрибуты или методы. Ещё setup() может вернуть объекты явно, и такие объекты станут сразу отображены в глобальной зоне видимости всех элементов компонента, аналогично props-ам.
Да, спасибо. Стоило бы все же указать источник. Так вот, эта диаграмма подтверждает то, что компонента на этапе функции setup() не существует. В диаграмме даже отмечено цветом то, что функция Setup() (синий) не входит в список Component Lifecycle Hooks (красный), о которых рассказывается в текущей статье.
<zanuda mode="on">
В setup() компонента, как такового (тот самый this), еще не существует. Потому setup() не является событием жизни компонента. А статья именно про жизненный цикл, так что - все норм.
</zanuda>
В принципе любой объект питона уникален, так что
я_точно_синглетон_dict()
можно тоже назвать Cинглетоном. Единый объект класса доступен каждому объекту через type(obj) или через дандер и часто используется как хранитель единого состояния для порожденных объектов, что, собственно, является одной из причин создания Singleton. Потому я субъективно отношу классы к Singleton-объектам.Нет. Класс не наследует свойства класса type, а является производной работы type. В качестве доказательства: по умолчанию у класса нет методов что есть у type, хотя их можно добавить.
Еще раз. Класс не является наследником type, он является результатом работы функции конструктора класса. На примере:
В примере вызывается функция конструктор, создающая в локальной области видимости класс с именем
"Name". Unbounded функции "mymethod" нет в методах базовых классов (если они переданы), и эта функция будет добавлена конструктором type к классу, да, грубо говоря добавлена в MyClass.__dict__, но если попробовать сделать это вручную получим
'mappingproxy' object does not support item assignment. Так что это не напрямую "заполнение словаря".Поправляю, Path наследован class PurePath(object), который, в свою очередь, ведет себя как metaclass, переопределяя __new__.
Переопределенный __div__ позволяет использовать синтаксис
Path('root') / Path('folder')
. В этом примере на команду __div__ происходитPath('root').parts + Path('folder').parts
. Но Path в реальности это просто маска двух классов:cls = PureWindowsPath if os.name == 'nt' else PurePosixPath
. Потому переопределив что-то у класса Path ничего не получим, надо переопределять у PureWindowsPath и PurePosixPath.Но спасибо @ValeryIvanov кое что я узнал про конструктор в python3, от отличается от Python2, а я это упустил.
Мне понравилось, спасибо!
Вот только "С вероятностью в 20 % вы вернётесь к этому фрагменту и будете его дорабатывать" - а можно узнать как считалась вероятность? A то тут Мартин Фаулерс беспокоится, он то в статьях о рефакторинге совсем другие величины указал.
Мне кажется, что автор не до конца понимает слово "legacy" (наследие). Все. Абсолютно все. И что было написано, и то, что будет написано, это legacy проекта. Да да, даже то, что запланировано, но еще не реализовано.
Именно потому legacy не существует. Есть проект. В том состоянии, в каком он есть. Что-то устарело, что-то нет. На примере Django. Проекту множество лет. В проекте остаются следы переходов с одной технологии на другую, хотелки, не реализованные с 0.95 версии, куски разнотипного кода от разных авторов, и лютый Си-тон в некоторых папках. Legaсy? Нет. Просто фреймворк, с которым я работаю долгое время.
Я могу представить, что под понятием "Легаси" в статье спрятался "Технический долг". Набор технических решений, требующих вмешательства специалиста. Но надо ли с этим бороться?
Исходя из слов Мартина Фаулера о необходимости рефакторинга - в любом проекте есть Zero Tolerance Tech-Debt Zone, Михаель Феатерс уточнил, как эту зону обнаружить статистически. Всю остальную часть проекта стоит оставить неизменной.
Потому, не надо ни с чем бороться. Легаси, она в голове. А в проекте есть тех долг, только малая часть которого должна быть выплачена.
P.s. У меня либо паранойя, либо в тексте некоторые куски кода похожи на GPT. Подозреваю первое.
"Ничего ты не знаешь, Джон Сноу"
Если рассматривать статью, как попытку разобраться в метаклассах. То зачет. Попытался.
Если рассматривать статью, как попытку разобраться в метаклассах. То мимо. Не разобрался.
Просто и понятно написано тут https://docs.python.org/3/reference/datamodel.html#metaclasses, но обычно это никто не читает.
Объекты класса как-то себя "проявляют" через методы и содержат атрибуты. Получить класс объекта можно командой type(obj).
Классы задают базовое поведение и задают шаблоны атрибутов для объектов класса. Любой класс это синглтон-объект, который как-то себя "проявляет" через методы и содержит атрибуты. Получить метакласс класса можно командой type(cls). Именно потому, что класс это тоже объект.
Метаклассы задают базовое поведение и задают шаблоны атрибутов для Классов, как объектов. Любой метакласс это синглтон-объект, который как-то себя "проявляет" через методы и содержит атрибуты. Получить Метакласс метакласса можно командой type(metacls). Именно потому, что метакласс - это тоже объект.
Можно продолжить. Создав мета мета класс, который будет задавать базовое поведение и задают шаблоны атрибутов для Мета классов, как объектов.
Фактически у нас есть только одно взаимоотношение. Класс --> Объект.
В списке выше это 2 --> 1, 3 --> 2, 4 --> 3, ...
Метакласс
MyMeta
в примере статьи, например, управляет поведением инициализации объекта классомMyClass
.Первый пример применения метакласса: фактически "Мета классом" мы "донастраиваем" работу некоторых методов класса. В Django метакласc модели превращает атрибуты-поля Класса Модели в атрибуты-дескрипторы объекта на моменте инициализации классом объекта. Если бы этого не происходило, то в объекте мы бы имели атрибут-поле (models.RelatedField). Но в объекте мы имеем models.DeferredDescriptor.
Метакласс имеет не так много методов, поскольку не так уж и много нам надо делать на этом уровне. Базовый метакласс, это который type, например, при создании любого класса добавляет ему набор стандартных дандер-методов, типа __getattr__, и т.п. В твоем примере ты создал
class MyClass()
ничего ему не объявил, а у класса есть метод __str__. Спасибо метаклассу type который по умолчанию является конструктором класса и делает всю грязную работу за нас.Это может пригодиться, если хочется поменять некоторые атодобавляемые методы у ВСЕХ классов этого метакласса. Опять Django. Добавляемый метод __eq__ у класса модели переопределен. Теперь в классе это поведение поменяно. И объекты этого класса в момент сравнения начинают меряться obj.pk ? obj2.pk вместо стандартного поведения id(obj) ? id(obj2)
Вот второй пример применения метакласса: вместо ручного прописывания методов у каждого дочернего класса, делаем это через метакласс. Кстати, я предпочитаю использовать для этого миксины. В dataclass - это же делают через декоратор.
Поскольку Метакласс позволяет переопределять дандер и не только методы на момент объявления класса (это же возможно сделать и миксином и наследованием и декоратором) то можно переопределить "__div__" и всю остальную математику и получить класс Path из Pathlib который реализует DSL, в данном случае это привычный file system syntax. Но важно, что этот класс на самом деле - это несколько классов PosixPath, WindowsPath etc. Без метакласса надо было бы переопределять математику в каждом из них ручками. Количество кода увеличилось бы. А через метакласс удалось соблюсти DRY да и KISS тоже где-то рядом.
По сути метакласс - это всегда синтаксический сахар сильно уменьшающий количество кода, который надо писать. Тут нет магии или мистики. Это просто. Но не для всех.
Верно. Но этот Exception в коде никак не обрабатывается. Я это имел ввиду под "не работает".
Чет не торт:
В примере:
Неужели только у меня екнула мылсь о тотальной ненужности
compute_length
как и тестов к этой функции :Про падающий
len(None)
отписал ниже.Далее, в оригинале непонятно что имел ввиду автор тут:
Из документации:
str.len()
возвращает новую серию, содержащую длины строк исходной серии. Таким образом, исходная серия остаётся неизменной. Так что это результат с числами вместо строк, о какой копии или не копии может идти речь?Незнание базового Python тоже удручает:
Да, точно, давайте создадим Safe Max функцию:
Вместо того что бы почитать документацию про max тут:
Ну и, напоследок, про "пойманный баг". В оригинале автор(
ка) прежде, чем работать с данными, их не провалидировала. По тестам видно, что данные - это набор строк, который вероятно, может содержать нулевые значения. Разумеется, можно на функции просчетаlen
возвращать 0 если передано значениеNull
. Это, кстати, придется делать для всех функций падающих наNull
. Ещё можно функцию применять не кdf.Name
а кdf.Name.str
. Падатьlen
уже не будет, потому как будут передаваться'None'
. Но результаты плачевные, это видно тут:На каждое
None
значение в исходной серии значений получим 4, вместо 0. Но в тестах это пропущено.Так что... Товарищи!
Мойте рукиВалидируйте данные перед едой! Ну и про тесты не забывайте, а то - тут одно протестировали, там другое... не надо так.p.s. За перевод спасибо!
if / elif
стильно/модно/моледежно заменять на словарь:для
CPU.NOP
надо, правда, поменять сигнатуру что бы принималaddress
или завернуть в lambda безадресные команды:А так спасибо @Yura_FX вспомнил детство за 8051 архитектурой. Было клево и безоблачно... ээх.
Скорее всего, что подход "би"-маппинга стоит решать на уровне получения объектов из БД. Это работает, если возможно контролировать БД всех систем.
В остальных случаях идея может быть улучшена, если известна система "источник" и система "назначение", а они известны в текущем примере, поскольку в этом случае маппинг выполняется с помощью только одного словаря "sourse: destination" и исключено появление каких-либо ошибок по ключам, о которых писали предыдущие комментаторы.
PS. Код не работает, если values в
mapping: dict
не hashable. А на __init__ это не проверяется.Загнанных лошадей пристреливают, не правда ли?
@SLY_G я все понимаю, запары и всякое такое. Но персидский залив в Америке это сильно! По английской статье понятно что речь идёт о южном побережье США, это Мексиканский залив. Я про подпись под картинкой о частоте ударов молнией.
Согласен, я ещё не понял зачем заворачивать responce с повторением всех атрибутов/методов. Почему тогда не передать нормальный response, тот что лежит в _response
Мод zanuda снимается. Не так прочел код. Там падение на .get
Но замечание про результат падения остается: при user.permissions.no_key.create_users непонятно, где выпал Еxception и выпал ли он. Я имею в виду, что None характеризует что (пустой словарь) or (нет user) or (user none) or (user пустой слолварь) or (user не содержит permissions) or ... продолжи сам.
Все что делает FastAPI - это прикручивает валидацию входящих данных через Pydantic. У Marcelo есть хороший доклад на PyConIT 2023 о том, что именно делает FastAPI.
Если мне не нужна валидация, поскольку я пишу endpoints для второй части приложения с CQRS-архитектурой, то выбор FastAPI выглядит очень спорно, я писал на хабре про низкую скорость работы FastAPI на выдачу данных.
Что я хочу сказать - выбор технологии должен быть согласован с задачей. FastAPI из коробки решает малую часть задач, как и Sanic, LiteStar ... Предлагать FastAPI, как альтернативу Django, это, на мой взгляд, более чем недальновидно, поскольку Django из коробки без дополнительных батареек решает намного больший спектр задач и довольно эффективно, чем FastAPI даже с батарейками.
Для пруфов - реализация мультиязычности, реализация GEO, классы представлений. Напрмер для работы с классами представлений в FastAPI - либо ставить FastAP utils, либо написать свой. А в таком случае зачем мне враппер враппера?
@asedoy Предлагаю закончить этот holy-war тут. Мне кажется, что лучше написать статью про свое Альтернативное видение стека для питониста‑джуна 2024, чем спорить в комментах.
Unittest пока имеет очень существенное преимущество перед pytest. Скорость выполнения тестов. На удобство становится пофиг, когда на precommit висит test all, и 1200 тестов. ..в качестве примера возьмем Shoop. Мы его на новую django переезжали, можно найти мой issue об этом. 1200 тестов в 4 потока на pytest 22 минуты. На unittest - около 7.
То что FastAPI знают больше Sanic, не объясняет выбор, поскольку непонятен сам выбор "малоизвестности". Если звезды не катят, тогда что? Я признаю, что Marcelo Trylesinski вкладывается на 100% в маркетинг FastAPI. Но FastAPI это просто wrapper к Starlette. Тогда почему не просто Starlette.
@lorerv не завершил предудущий коммент, а править уже нельзя.
Блок с названием "Генераторы для ..." и без генераторов. Вероятно, планировалось так:
В первой функции я иду по итератору выдающему пачки users. уже позже понял что можно , что еще проще, если использовать
generator.send()
, но это оставим для следующей статьи.Во второй функции я иду по итератору выдающему пачки users и стопаю итерацию после выдачи последнего допустимого user_chank. Хотя использовать
itertools.batched
в этом случае более предпочтительно.Малоизвестные кому? Малоизвестные кто? PonyOrm? Pylint? Conda? Ладно, вместо Litestar возьмем Sanic, а вместо Rue возьмем pipx. Сойдут за "более известные" библиотеки?
Ruff появился недавно, Flake8 в 2010, Pylint в 2001 - кто более проверен, кто более известен, у кого больше звезд и наработок?
И кстати, а что не так с unitest и почему его не надо сравнивать с pytest?
Я к тому, что у всех свое болото, и то, что неизвестно мне, не означает, что это неизвестно никому. Потому выбор предложения для альтернативы примерам в статье пока считаю не обоснованным.
Я много лет работаю консультантом на сложных Python проектах, вот дорос даже до доклада на нескольких конференциях и могу сказать, что именно упустил автор: Любое улучшение должно быть выполнено в стилистике кода проекта, а не в стилистике специалиста выполняюшего рефакторинг.
Как было упомянуто выше, тернарник или конкатенация проверочного условия через OR не имеют категорий "лучше-хуже". Есть четкая характеристика "используемая стилистика проекта", не важно, есть у вас отдельный StyleGuide или только код проекта, который, сам по себе, и есть StyleGuide. И предлагать чуждые проекту syntactic sugar конструкции - это ухудшить DevEx, который на большом проекте и так уже ниже плинтуса.
Кстати, при использовании тернарного оператора мы защищены от ошибки смены объекта в отличие от конкатенации условий:
в этом случае myobj будет указывать или на старый объект или на новый.
myobj = myobj or type(MyObj)()
А в этом случае указатель будет поменян только если myobj еще не был создан.
myobj = myobj if myobj is None else type(MyObj)()
Разумеется, MyObj специальный класс с переопределенным "
__bool__
" или "__eq__
"Про comprehensions я писал и рассказывал на PyCon: если итерироваться надо будет один раз, то это должен быть генератор. Если несколько раз - то, вероятно, выбран не тот алгоритм. В проектах, где я провожу code review, неоходимость размещения в памяти сразу всего сгенеренного массива разработчик должен аргументировать. Иначе, будь добр, ставь генератор.
Прежде чем рефакторить return True False в несколько строк, так же, сначала смотрим в код или styleguide.
Ну и про поиск в Словарях. Можно еще сделать так:
Вариант возврата
None
на несуществующий атрибут/ключ - это за гранью, поскольку нет возможности понять, значение ключа не передано или ключа нет вообще, но оставим это на совести атора.Кстати <zanuda mode="on">:
В статье в примере этого раздела ошибка: при попытке получения в словаре несуществующего ключа быдет выброшен exception
KeyError
, а неAttributeError
.</zanuda>
Желаю автору успехов в работе с кодом. Ему это точно пригодится!