Pull to refresh
71
0
Tishka17 @Tishka17

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

Send message

У вас решение за квадрат (из-за срезов)

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

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

Как будто windows 7 и windows XP поддерживаются сами по себе

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

Он не работает.

  1. lock в данном случае содержит результат блокировки, а сам лок

  2. Если бы там был все таки Лок, мы бы получили двойную блокировку (одна из-за контекстного менеджера, вторая из-за aquire)

  3. Если бы мы взяли RLock вместо Lock, это позволило бы внутри одного контекста повторно вызвать aquire, но код бы все равно не имел смысла, потому что Лок используется только в одном потоке

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

А почему? Если у вас сложные вычисления, то у вас скорее всего не будет переключения контекста, наоборот должно работать "быстрее". Другой вопрос, что никакой конкурентности при этом не будет

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

У меня есть проект, тоже позволяющий описывать UI декларативно, но я не стал пока выносить это в YAML, а предлагаю описывать в виде python-кода (хотя ребята показывали как они генерируют мои объекты из yaml). И тут уже есть свобода создания кастомных компонентов, наследования для внедрения своей логики и т.п. Велкам на гитхаб: https://github.com/Tishka17/aiogram_dialog/. И вот поверх этого уже можно генерировать превью, диаграмму переходов (с определенными ограничениями). Простенькое демо нескольких фич доступно тут: https://t.me/aiogram_dialog_demo_bot (код демо).

Условно у меня это выглядит вот так:

Dialog(
    Window(
        Const("Greetings! Please, introduce yourself:"),
        StaticMedia(
            path=os.path.join(static_dir, "python_logo.png"),
            type=ContentType.PHOTO,
        ),
        MessageInput(name_handler, content_types=[ContentType.TEXT]),
        MessageInput(other_type_handler),
        state=DialogSG.greeting,
    ),
    Window(
        Format("{name}! How old are you?"),  # dynamic text
        Select(  # dynamic buttons list
            Format("{item}"),
            items="ages",  # retrieved from getter function
            item_id_getter=lambda x: x.id,
            id="w_age",
            on_click=on_age_changed,
        ),
        state=DialogSG.age,
        getter=get_data,
    ),
)

Питон НЕ проверяет тайпхинты в процессе выполнения. Надо использовать ДО запуска отдельные инструменты типа mypy/pyright.

"hello".captialize() и str.capitalize("hello") работает одинаково, используя один и тот же код.

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

Даже лучше соответствует определению интерфейса, так как не заставляет указывать что вы его реализуете (в отличие от джавы)

Если вы не используете пути как объекты, а везде вам нужны строки, то все возжности покрываются os.path.

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

В рамках данной статьи был константа с прямым слэшом /, это теоретически может привести к некорректному поведению, когда его будут комбинировать дальше с путями в ОС Windows, содержащими обратный слэш \

Всё ещё не понимаю, почему её форсят некоторые люди:

  • у нее достаточно сложная минимальная настройка из-за необходимости интеграции с logging

  • у нее не очень высокая гибкость (коллеги жаловались на сложности кастомизации хэндлеров)

  • у нее ужасно многословные трейсы по дефолту

Кажется, всё таки стоит написать большой комментарий.

1) Используйте генераторы
Как уже обсуждали выше это может замедлить программу или внести дополнительные негативные эффекты ввиду того, что это итератор. При этом генератор действительно позволяет сэкономить память и иногда код с ним получается проще, в других местах же они не подходят.

3) Увеличивайте производительность вашего кода
Увеличивайте, если это дает что-то бизнесу. Ваше время не бесплатное. Вы можете потратить больше времени и денег, чем сэкономите за счет ускорения кода. Иногда скорость выполнения критчна, иногда нет. Стоит понимать в какой вы ситуации.
Кроме того, прежде чем заниматься оптимизацией неплохо бы понять что мы ускоряем и есть ли тут потенциал. Если у вас 99% времени занимает расчет на numpy или ожидании времени ответа СУБД, то никакие слоты и сабпроцессы не помогут.
Профилируйте код, ищите узкие места, оптимизируйте то, что имеет смысл оптимизировать. При этом, само собой, при наличии двух вариантов реализации не стоит выбирать просто так тот, который медленнее. Если оптимизация дается вам бесплатно (без значительного ухудшнения поддерживаемости кода и сроков), стыдно ей не воспользоваться

3.1) Многопроцессность
Использование нескольких процессов по сравнению с потоками может привести к дополнительным накладным расходам на пересылку данных, невозможности использовать общие данные.
При использовании же алгоритмов, реализованных в нативных библиотеках, которые отпускают GIL это просто не имеет смысл.
Зачастую наши приложения деплоятся в одноядерном окружении и масштабируются горизонтально, не увеличивая число ядер на отдельном инстансе.
При выборе подхода учитывайте алгоритмы, реализацию, окружение и обязательно профилируйте код!

3.3) Другие интерпретаторы
Это опция интересная, но доступная далеко не всегда. Плюс, как уже выше сказали, надо понимать, что использование альтернативного интерпрrтатора может ограничить вас в использовании библиотек, значительно усложнить процесс деплоя или нарушить стабильность кода.
Иногда так же использование другого интерпретатора приводит к повышению требований к ОЗУ.

5) Используйте срезы
Используйте срезы там где вам нужен срез. Не забывайте, что взятие среза - копирование части списка, это легко может превратить ваш алгоритм с O(n) в O(n^2).
Стоит напомнить про существование islice.

6) Храните данные правильно
Используйте константы на уровне модуля там где вам нужна просто константа.
Используйте датаклассы, там где вам нужна структура данных, потенциально имеющаяся в нескольких экземплярах.
Используйте словари там, где вам нужны произвольные гомогенные ключи, а классы там где у вас поля имеют разный смысл или тип.
Используйте Enum там где у вас выбор значений из нескольких вариантов и других быть не может.
Не забывайте про \ и / в путях.

7) Используйте максимум возможностей ООП

7.1) Используйте утиную типизацию, но выделяйте абстракции
У нас всё таки Python. Если необходимо - декларируйте абстрактные классы или Protocol

7.2) Используйте наследование и композицию.
Запомните, что наследование делает структуру кода более хрупкой и должно применяться с осторожностью. Это полезный инстурмент, но зачастую композиция дает более стабильное и гибкое решение. Почитайте про различные паттеры проектирования, в частности декоратор и стратегию

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

8) Попробуйте кусочек функционального программирования
В целом в Python удобнее НЕ писать в ФП стиле, но есть оговорки.

map удобен когда у вас уже есть функция, но иногда его пытаются использовать с лямбдой, что только замедляет код. Используйте comprehension. Кроме list comprehension есть set comprehension и dict comprehension.

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

Пользуясь случаем, хочу поделиться своим каналом в telegram, где я рассказываю как лучше делать некоторые вещи: https://t.me/advice17

не исправил

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

Я встречал код, где вовсю использовали генераторы, но затем делали itertools.tee, для создания копий и который в результате разворачивал их в памяти.

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

Использование датакласса для хранения константы - очень плохое и бессмысленное действие.

Во-первых, классы нужны для того чтобы создавать экземпляры. У вас они не создаются
Во-вторых, датакласс нужен для того чтобы сгенерировать классу такие методы как __init__ и __eq__, что снова не используется.
В-третьих, вы упоминаете frozen, но он влияет именно на проведение экземпляра класса, которого нет. Да и создать можно много экземпляров.

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

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

"Слайсы" слышал только применительно к языку го, где это отдельный тип объекта. Всегда встречал именно "срезы".

Вложенные функции имеют смысл только для использования закмыканий при реализации декораторов, колбэков и т.п. (Частичное применение делается через functools.partial). В остальных случаях это ошибка: их использование как минимум затрудняет тестирование.

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

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity

Specialization

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