Как стать автором
Обновить
29
0.2
Максим @danilovmy

Программист разработчик

Отправить сообщение

Цитируя документацию, ссылку на которую я привел в первом ответе:

setup() не имеет доступа к компоненту экземпляра — thisбудет иметь значение undefinedвнутри setup()

Инициализация объекта происходит непосредственно перед вызовом 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() тоже синглетон? Класс это экземпляр класса type или одного из его наследников, не более.

В принципе любой объект питона уникален, так что я_точно_синглетон_dict() можно тоже назвать Cинглетоном. Единый объект класса доступен каждому объекту через type(obj) или через дандер и часто используется как хранитель единого состояния для порожденных объектов, что, собственно, является одной из причин создания Singleton. Потому я субъективно отношу классы к Singleton-объектам.

Класс это экземпляр класса type или одного из его наследников, не более.

Нет. Класс не наследует свойства класса type, а является производной работы type. В качестве доказательства: по умолчанию у класса нет методов что есть у type, хотя их можно добавить.

type ничего не добавляет своим наследникам. Единственное что type.new делает, так это создаёт новый экземпляр переданного класса и заполняет его словарь, используя переданные параметры. Ну и валидацию выполняет, чтобы от некоторых типов не наследовались, но это не суть важно.

Еще раз. Класс не является наследником type, он является результатом работы функции конструктора класса. На примере:

 MyClass = type('Name', (__bases__,), {'mymethod': lambda *args, **kwargs: 'hi'})

В примере вызывается функция конструктор, создающая в локальной области видимости класс с именем "Name". Unbounded функции "mymethod" нет в методах базовых классов (если они переданы), и эта функция будет добавлена конструктором type к классу, да, грубо говоря добавлена в MyClass.__dict__, но если попробовать сделать это вручную получим'mappingproxy' object does not support item assignment. Так что это не напрямую "заполнение словаря".

Насколько я знаю, pathlib никаким образом не использует метаклассы. Поправьте, если я ошибаюсь.

Поправляю, Path наследован class PurePath(object), который, в свою очередь, ведет себя как metaclass, переопределяя __new__.

И даже если использует, то зачем в теории может понадобиться переопределять div на каком-нибудь PathType, а не на самом Path?

Переопределенный __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, но обычно это никто не читает.

  1. Объекты класса как-то себя "проявляют" через методы и содержат атрибуты. Получить класс объекта можно командой type(obj).

  2. Классы задают базовое поведение и задают шаблоны атрибутов для объектов класса. Любой класс это синглтон-объект, который как-то себя "проявляет" через методы и содержит атрибуты. Получить метакласс класса можно командой type(cls). Именно потому, что класс это тоже объект.

  3. Метаклассы задают базовое поведение и задают шаблоны атрибутов для Классов, как объектов. Любой метакласс это синглтон-объект, который как-то себя "проявляет" через методы и содержит атрибуты. Получить Метакласс метакласса можно командой type(metacls). Именно потому, что метакласс - это тоже объект.

  4. Можно продолжить. Создав мета мета класс, который будет задавать базовое поведение и задают шаблоны атрибутов для Мета классов, как объектов.

Фактически у нас есть только одно взаимоотношение. Класс --> Объект.

В списке выше это 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 в коде никак не обрабатывается. Я это имел ввиду под "не работает".

Чет не торт:

В примере:

def compute_length(word: str) -> int:
    return len(word)

def prepare_data(df: pd.DataFrame) -> pd.DataFrame:
    return pd.concat([
        ...
        df.name.apply(compute_length).rename("name_len"),
        ...
    ],...)

Неужели только у меня екнула мылсь о тотальной ненужности compute_length как и тестов к этой функции :

def prepare_data(df: pd.DataFrame) -> pd.DataFrame:
    return pd.concat([
        ...
        df.name.apply(len).rename("name_len"),
        ...
    ],...)

Про падающий len(None) отписал ниже.

Далее, в оригинале непонятно что имел ввиду автор тут:

eries = df["name"].str.title()  # not a copy whatsoever (в любом случае не копия)

Из документации: str.len()возвращает новую серию, содержащую длины строк исходной серии. Таким образом, исходная серия остаётся неизменной. Так что это результат с числами вместо строк, о какой копии или не копии может идти речь?

Незнание базового Python тоже удручает:

def find_max_name_length(df: pd.DataFrame) -> int:
    df["name_len"] = df["name"].str.len()  # побочный эффект
    return max(df["name_len"])

@pytest.mark.parametrize("df, result", [
    (pd.DataFrame({"name": []}), 0),  # упс, здесь тест упадет

Да, точно, давайте создадим Safe Max функцию:

def find_max_element(collection: Collection) -> int:
    return max(collection) if len(collection) else 0

Вместо того что бы почитать документацию про max тут:

def find_max_element(collection: Collection) -> int:
    return max(collection, default=0)

Ну и, напоследок, про "пойманный баг". В оригинале автор(ка) прежде, чем работать с данными, их не провалидировала. По тестам видно, что данные - это набор строк, который вероятно, может содержать нулевые значения. Разумеется, можно на функции просчета len возвращать 0 если передано значение Null. Это, кстати, придется делать для всех функций падающих на Null. Ещё можно функцию применять не к df.Name а к df.Name.str. Падать len уже не будет, потому как будут передаваться 'None'. Но результаты плачевные, это видно тут:

def create_name_len_col(series: pd.Series) -> pd.Series:
    return series.str.len()

На каждое Noneзначение в исходной серии значений получим 4, вместо 0. Но в тестах это пропущено.

Так что... Товарищи! Мойте руки Валидируйте данные перед едой! Ну и про тесты не забывайте, а то - тут одно протестировали, там другое... не надо так.

p.s. За перевод спасибо!

if / elif стильно/модно/моледежно заменять на словарь:

cmd_dict = {
    0x0: CPU.NOP, 
    0x6: CPU.INC,
    ...
}

...  # Где-то в коде run() 
cmd = cmd_dict.get(opcode)
if not cmd:
  raise UncnownOpCode()  # или print + return в зависимости от реализации.
address = memory[self.pc + 1]
cmd(self, address)
...

для CPU.NOP надо, правда, поменять сигнатуру что бы принимал address или завернуть в lambda безадресные команды:

cmd_dict = {
    0x0: lambda *args, **kwargs: CPU.NOP, 
    0x6: CPU.SUB,
    ...
}

А так спасибо @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 не завершил предудущий коммент, а править уже нельзя.

Блок с названием "Генераторы для ..." и без генераторов. Вероятно, планировалось так:

def user_paginate(client):
    limit, page = 3, 0
    for page, users in enumerate(iter(lambda:client.get_all_users(page, limit))):
        yield users

def user_limit_paginate(client, max_pages=3):
    for page, users in enumerate(user_paginate(client), 1):
        yield page, users
        if page >= max_pages:
            return

# gde to v code
all_paginated_users = yield from user_paginate(client)
paginated_users = yield from user_limit_paginate(client, 255)

# or with itertools, you are dont need user_limit_paginate function at all
import itertools
paginated_users = yield from itertools.batched(user_paginate(client), 255)

В первой функции я иду по итератору выдающему пачки 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.

Ну и про поиск в Словарях. Можно еще сделать так:

class DataDict:
    def init(self, **kwargs):
        self.dict.update({key: DataDict(**val) if isinstance (val, dict) else val for key, val in kwargs.items()})
    
    def __getattr__(self, name):
        return None

# где то в коде
value = DataDict(**data).user.permissions.create_users

Вариант возврата Noneна несуществующий атрибут/ключ - это за гранью, поскольку нет возможности понять, значение ключа не передано или ключа нет вообще, но оставим это на совести атора.

Кстати <zanuda mode="on">:

В статье в примере этого раздела ошибка: при попытке получения в словаре несуществующего ключа быдет выброшен exception KeyError, а не AttributeError.

</zanuda>

Желаю автору успехов в работе с кодом. Ему это точно пригодится!

1
23 ...

Информация

В рейтинге
1 945-й
Откуда
Zams, Tirol, Австрия
Дата рождения
Зарегистрирован
Активность

Специализация

Backend Developer, Fullstack Developer
Lead
От 7 000 €
Python
Django
Ajax
OOP
Design patterns
Vue.js
JavaScript
HTML
CSS