Задать вопрос
  • Как вернуться из модуля обратно в main?

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

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

    module1.py
    # бота передаём как параметр внутрь функции install_module
    # если у тебя будут другие глобальные ресурсы, скажем, соединение с СУБД, можно передавать их также
    def install_module(bot) -> None:  
        # да, мы описываем функцию прямо внутри другой функции. Так можно
        @bot.message_handler(commands=['some_command', ])
        def my_command(message):
            ...  # тут логика команды
    
        # и ещё одну...
        @bot.message_handler(commands=['other_command', ])
        def my_other_command(message):
            ...  # тут логика команды

    Тогда в main.py будет что-то вроде
    import telebot
    bot=telebot.TeleBot('TOKEN')
    # импортируем и активируем модуль
    import module1
    module1.install_module(bot)  # install_module() модуля должна вызываться строго однажды
    # модулей может быть несколько
    import module2
    module2.install_module(bot)
    # когда всё установлено, запускаем бота
    bot.infinity_polling()

    Идею можно развить таким образом: пусть твои модули лежат не рядом с main.py, а в подпапке modules. Тогда можно сделать что-то такое, чтобы автоматически подгрузить все модули из этой папки при старте бота.
    from pathlib import Path
    import sys
    import importlib
    
    import telebot
    
    bot=telebot.TeleBot('TOKEN')
    
    MAIN_DIR = Path(sys.argv[0]).parent.resolve()  # папка где лежит скрипт бота
    MODULES_DIR = MAIN_DIR / 'modules'  # лежащая рядом папка modules
    for item in MODULES_DIR.glob('*'):   # перебираем файлы и папки в папке modules
        # игнорируем папки и файлы, начинающиеся с _ или с .
        if item.name.startswith('_') or item.name.startswith('.'):
            continue
        # item - это имя пригодного для импорта модуля?
        if (item.isfile() and item.name.endswith('.py')) or (item.isdir() and (item / '__init__.py').isfile()):
            # да импортируем и активируем модуль
            module = importlib.import_module('modules.'+item.name)
            module.install_module(bot)
    
    # когда всё установлено, запускаем бота
    bot.infinity_polling()
    Ответ написан
    2 комментария
  • Почему в параметр функции идёт только последний индекс?

    Vindicar
    @Vindicar
    RTFM!
    Поздравляю, ты попался на лямбду.
    Лямбды сохраняют ссылки на переменные, а не их значения.
    lambda _: set_default_microphone(index) сохранит ссылку на index, а потому все лямбды будут видеть одно и то же значение index, которое было установлено последним.
    Это можно обойти, сохранив ссылку при объявлении лямбды. Самый простой способ - засунуть сохраняемое значение в значение по умолчанию для неиспользуемого параметра. Значения по умолчанию для параметров вычисляются один раз при объявлении функции, в т.ч. лямбды.
    Например, так:
    lambda _, *, ind=index: set_default_microphone(ind)

    Ну или даже так:
    lambda _, *, index=index: set_default_microphone(index)
    Ответ написан
    2 комментария
  • Как мне проигнорировать этот Подкаталог "old" и прочитать данные только из списка?

    Vindicar
    @Vindicar
    RTFM!
    from pathlib  import Path # входит в поставку питона, не сторонняя
    
    base_path = Path("data")  # путь до каталога с данными
    for item in base_path.glob('*'):
      if item.is_dir() and item.stem != 'old':
        # тут что-то делаем с путём item. Например:
        for kml_file in item.glob('*.kml'):
          print(kml_file.resolve())  # выводим полный путь к файлу
    Ответ написан
  • TypeError: Parameters to generic types must be types. Got Ellipsis.?

    Vindicar
    @Vindicar
    RTFM!
    Какая у тебя версия питона? Как минимум в 3.12 конструкция List[Dict[str, ...]] поддерживается. Может, и в более ранних тоже.
    Ответ написан
  • Решил попробовать в написание бота для TG, выходит ошибка RuntimeError?

    Vindicar
    @Vindicar
    RTFM!
    Без агрессии нельзя, это же интернет. Если тут не посылают, значит, что-то стряслось. =)

    Основой асинхронной программы является рабочий цикл (event loop). Он должен быть строго один. Я подозреваю, что у тебя создаётся более одного рабочего цикла, так как ты делаешь несколько вызовов get_event_loop() в придачу к asyncio.run().

    Я бы изменил код следующим образом: пусть класс базы данных принимает пул соединений как параметр конструктора. Аналогично, создай в основном файле глобальную переменную, которая будет хранить экземпляр этого класса. При создании присвой переменной None.

    А вот внутри main() уже делай всё остальное: создай экземпляр пула соединений, создать экземпляр класса БД (и закинь его в глобальную переменную), а потом запускай бота. Тогда у тебя все асинхронные операции будут происходить в рамках одного вызова main(), а значит, гарантированно в одном рабочем цикле.
    Ответ написан
    Комментировать
  • Методы для поиска объектов на изображениях?

    Vindicar
    @Vindicar
    RTFM!
    почитать подробно про методы

    Хотелось бы понять какой метод, для какой задачи больше походит

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

    Навскидку, ключевые вещи на которые нужно смотреть это трудоёмкость, инвариантность и поддержка множественных экземпляров. Первое определяет, насколько сложно подготовить детектор объектов этим методом. Второе определяет, сломается ли метод если искомый объект повернуть/увеличить/ярко осветить/и т.п. Третье определяет, сломается ли метод, если искомый объект присутсвует в нескольких экземплярах.

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

    Каскады Хаара. Требуют контрастных объектов, не справляются с поворотами, могут быть чувствительны к масштабы. Долго обучаются, но довольно быстро работают. Сейчас их редко применяют.

    Методы, основанные на локальных особенностях. Требуют "пёстрых" объектов с множеством заметных деталей (в идеале контрастных углов), и не справляются с деформируемыми объектами или очень разными ракурсами. Легко справляются с произвольными трансформациями, но ломаются на множественных экземплярах. Это придётся обходить, обрабатывая изображение по частям. Зато если объект простой (условно, обложка книги), им обычно хватает одного изображения.

    Свёрточные нейронки вроде семейства YOLO. Можно научить много на что, и скорость работы у них стабильная, но нужна большая размеченная база для обучения. Причём если в базе не было скажем, повёрнутых изображений, нейронка их не научится распознавать. Отчасти обучающую базу можно расширить джиттерингом, но готовить её всё равно придётся, причём речь идёт о сотнях и тысячах изображений.
    Ответ написан
    1 комментарий
  • Что делать, при печати, звук ошибки системы в PyCharm?

    Vindicar
    @Vindicar
    RTFM!
    Переключение по Alt-Shift? Это не только в pycharm такое.
    В винде нажатый и отпущенный Alt - это выход в главное меню окна. Приём это для большинства окон работает. А когда ты в главном меню, набор клавиш пытается выбрать подходящий пункт в главном меню. Если такого пункта нет - будет звук ошибки. И это еще хороший варинт, можешь выбрать фиг знает что.
    Я не до конца уверен при каких обстоятельствах Alt-Shift триггерит главное меню, но этого было достаточно, чтобы все свои компы перенастроить на Ctrl-Shift. Ну или точнее, использую AutoHotKey чтобы по нажатию CapsLock прожимался Ctrl-Shift, а это комбо уже переключате раскладку.
    Ответ написан
    Комментировать
  • Как найти паттерн на картинке с OpenCV?

    Vindicar
    @Vindicar
    RTFM!
    Во-первых, оформи код, нечитаемо. Кнопка </> в помощь.
    Во-вторых, matchTemplate(), насколько я знаю, не-инвариантна к поворотам и масштабу. Иными словами, поворот или изменение размера целевого объекта сломают сопоставление.
    В-третьих, на показанном кадре более одного экземпляра целевого объекта. Так предполагается или нет? Если предполагается, сколько экземпляров ожидается? Потому что разница очень большая с точки зрения методики.

    Варианта тут три.
    1. пытаться обучать под задачу нейронку. А лучше дообучить существующую, скажем, YOLOv5. Но тебе потребуется минимум несколько сотен размеченных изображений объекта в разных комбинациях, плюс в 2-3 раза больше похожих изображений без целевого объекта. Готовить такую базу будет утомительно, да и само обучение требует понимания что ты делаешь.
    2. попробовать зафиксировать угол поворота или масштаб (т.е. допустить, что он всегда одинаков). Тогда другой параметр можно будет подбирать. Например, мы фиксируем масштаб и делаем 16 изображений объекта в разных поворотах, а потом поочерёдно ищем каждый вариант на кадре. Потом анализируем силу откликов - сколько их, насколько они сильные и т.д. Скорее всего, будет медленно
    3. взять за основу поиск по локальным особенностям. Он справляется с масштабом и поворотом, но не справляется с несколькими экземплярами объекта. Это можно забороть, если использовать скользящее окно. Иными словами, находим на кадре узнаваемые точки - локальные особенности (желательно достаточно много и достаточно плотно), используя алгоритмы вроде ORB или SIFT. Затем выбираем те из них, которые попадают в прямоугольную рамку-окно. Затем сверяем эти особенности с особенностями объекта, используя RANSAC или подобный метод. Если получилось хороше совпадение - значит, в этой рамке есть объект или значительная его часть, и мы можем оценить его позицию в кадре в целом. Повторяем процесс, сдвигая рамку, пока оно не "обойдёт" всё изображение. Тоже может быть небыстро, так как нам требуется неоднократный поиск по картинке.
    Ответ написан
    Комментировать
  • Сохранение модели tensorflow?

    Vindicar
    @Vindicar
    RTFM!
    Размер файла (при одном и том же формате и точности) определяется количеством коэффициентов модели.
    Так что если ты одну и ту же по структуре модель обучаешь, размер у неё будет одинаковый.
    Ответ написан
    7 комментариев
  • Как изменить код для работы с видео?

    Vindicar
    @Vindicar
    RTFM!
    Тебе модель, скорее всего, отдаст экземпляр класса Detections.
    Если его посмотреть, там есть несколько полезных методов, например, render(), который возвращает список обработанных изображений с отрисованными объектами. А ещё у него есть свойства imgs, xywh и names, чтобы получить обработанные изображения, рамки и классы найденного. Экспериментируй с ними
    Не забываем, что модель рассчитана на подачу пачки изображений, просто эта пачка может содержать и только одно изображение. Отсюда и множественное число, и постоянные массивы.
    Ответ написан
    Комментировать
  • Как узнать что пишет пользователь после введённой команды(библиотека discord.py)?

    Vindicar
    @Vindicar
    RTFM!
    Открываем документацию к discord.py, изучаем.
    Вот тебе ссылка на нужный раздел.
    Искать информацию по незнакомой теме тоже нужно учиться.
    Ответ написан
    Комментировать
  • Установил библиотеку, но код не видит его, что делать?

    Vindicar
    @Vindicar
    RTFM!
    C:\Users\Владелец\OneDrive\Рабочий стол\faeil не делает ровным счётом ничего.
    Если ты пытаешься сменить текущий рабочий каталог, тебе нужно использовать команду cd.
    А так как твой путь содержит пробелы, его нужно заключить в кавычки.
    cd "C:\Users\Владелец\OneDrive\Рабочий стол\faeil"
    Ответ написан
    1 комментарий
  • Не работают кнопки в тг боте на python как починить?

    Vindicar
    @Vindicar
    RTFM!
    Проблема, я полагаю, та же что и всегда - несколько обработчиков с func=lambda call: True.
    Ответ написан
    Комментировать
  • Какие технологии надо для создания мессенджера?

    Vindicar
    @Vindicar
    RTFM!
    Ну во-первых, у тебя четыре задачи.
    1. Фронт. На каких платформах планируешь клиенты? Веб-клиент позволяет охватить много устройств, но он также может оказаться наиболее прожорливым по ресурсам. Если планируешь отдельные клиенты под отдельные платформы - надо смотреть отдельно под каждую платформу. Сразу исходи из того, что универсального решения не будет.
    Под веб, разумеется, надо учить веб-стэк - как вообще работает HTTP, основы HTML+JS+CSS, скорее всего, придётся ознакомиться с каким-то JS-фреймворком, потому что на голом JS удобно писать только достаточно простой клиент. Стоит также заглянуть в сторону вебсокетов.
    2. Протокол обмена. Их много разных - и это если не изобретать свой велосипед. Текстовые протоколы проще в отладке. Бинарные - компактнее (экономит трафик). Некоторые протоколы работают строго в рамках модели запрос клиента -ответ сервера (как HTTP), некоторые позволяют любому концу соединения проявлять инициативу. В твоей задаче запрос-ответ будет работать так себе. Читай про существующие универсальные протоколы обмена, такие как JSON-RPC или gRPC. Загляни в сторону REST. Да хоть старичка IRC посмотри. Важны моменты: многословность протокола (сколько надо передать байт по сети на N символов текста? А на N байт бинарного контента?), ограничения на передаваемые данные (например, можно ли передавать произвольные бинарные данные?), наличие библиотек для работы с протоколом, устойчивость к разрывам соединения, и т.д.
    3. Бэк (сервер). В твоей задаче бэку лучше быть событийно-ориентированным, поэтому надо понимать суть событийно-ориентированной архитектуры. Соответственно, исходя из выбора протокола, выбираешь язык, для которого есть библиотеки под этот протокол, и в идеале который тебе знаком. Разбираешься, как организуется работа с этими библиотеками.
    Продумываешь, какими сущностями ты будешь оперировать. Например, у тебя будут только чаты 1 на 1, или будут групповые чаты? А если групповые чаты, они будут одноранговые, или будут иметь структуру, типа серверов в Дискорде? Затем продумываешь, как ты это будешь хранить в базе данных.
    Да, с базами данных надо быть знакомым.

    Ну и задача №0, которую я описываю последней. Определи, под какую нишу ты делаешь свой мессенджер, кто будет им пользоваться, что для них будет важно? Условно, мессенджер, который предназначен для координации действий группы людей на реальной местности, это не то же, что месседжер, предназначенный для общения внутри корпорации, и не то же, что мессенджер, предназначенный для домовой/общажной локалки, где может не быть одного выделенного сервера.
    В частности, важным вопросом будет структура сети. Централизованная, как Телеграм? "Лес", где может быть несколько серверов, но они не вазимодействуют? Федеративная, как e-mail или matrix, где можно поднять свой сервер, и взаимодействовать с клиентами других серверов? Одноранговая (P2P), как Tox? Это сильно повлияет и на целевую аудиторию, и на устройство протокола.
    Ответ написан
    Комментировать
  • Статья закончена, а ответ на вопросы нет?

    Vindicar
    @Vindicar
    RTFM!
    Плохой образец для подражания выбрал - там почему-то код на питоне вперемешку с HTML-разметкой.
    Лучше поищи официальные доки и учись по ним.
    Ответ написан
  • Как выполнить шифровку при помощи шифра Вижинера?

    Vindicar
    @Vindicar
    RTFM!
    Делишь задачу на под-задачи. Допустим, у тебя есть строка-исходный текст text и строка-ключ key, а также список строк, содержащий алфавит языка alpha.
    Определяешь функцию, заменяющую символ по шифру:
    # принимает символ открытого текста, его позицию в тексте, строку-ключ и алфавит
    # возвращаем символ шифротекста
    def viginere_encode(text_char: str, i: int, key: str, alpha: list[str]) -> str:
      if text_char not in alpha:  # незнакомый символ?
        return text_char  # оставляем его как есть
      text_char_code = alpha.index(text_char)  # номер символа открытого текста в алфавите
      key_char = key[i % len(key)]  # символ ключа, соответствующего i-му символу открытого текста 
      key_char_code = alpha.index(key_char)   # номер символа ключа в алфавите
      cypher_char_code = (text_char_code + key_char_code) % len(alpha)  # номер символа шифротекста
      return alpha[cypher_char_code]  # возвращаем символ шифротекста

    А дальше применяем эту функцию к строке:
    cipher_text = ''
    for i in range(len(text)):
      cipher_text += viginere_encode(text[i], i, key, alpha)

    Вот и всё. Да, тут много чего можно оптимизировать, но я старался писать как можно проще.

    Полный пример
    alpha = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
    text = 'A SMOKE OF MOTHERLAND IS SWEET FOR US AND PLEASANT'
    key = 'SECRET'
    
    # принимает символ открытого текста, его позицию в тексте, строку-ключ и алфавит
    # возвращаем символ шифротекста
    def viginere_encode(text_char: str, i: int, key: str, alpha: list[str]) -> str:
      if text_char not in alpha:  # незнакомый символ?
        return text_char  # оставляем его как есть
      text_char_code = alpha.index(text_char)  # номер символа открытого текста в алфавите
      key_char = key[i % len(key)]  # символ ключа, соответствующего i-му символу открытого текста 
      key_char_code = alpha.index(key_char)   # номер символа ключа в алфавите
      cypher_char_code = (text_char_code + key_char_code) % len(alpha)  # номер символа шифротекста
      return alpha[cypher_char_code]  # возвращаем символ шифротекста
    
    # принимает символ шифротекста, его позицию в тексте, строку-ключ и алфавит
    # возвращаем символ открытого текста
    def viginere_decode(cipher_char: str, i: int, key: str, alpha: list[str]) -> str:
      if cipher_char not in alpha:  # незнакомый символ?
        return cipher_char  # оставляем его как есть
      cipher_char_code = alpha.index(cipher_char)  # номер символа шифротекста в алфавите
      key_char = key[i % len(key)]  # символ ключа, соответствующего i-му символу шифротекста 
      key_char_code = alpha.index(key_char)   # номер символа ключа в алфавите
      text_char_code = (cipher_char_code + len(alpha) - key_char_code) % len(alpha)  # номер символа открытого текста
      return alpha[text_char_code]  # возвращаем символ открытого текста
    
    
    cipher_text = ''
    for i in range(len(text)):
      cipher_text += viginere_encode(text[i], i, key, alpha)
    
    decoded_text = ''
    for i in range(len(cipher_text)):
      decoded_text += viginere_decode(cipher_text[i], i, key, alpha)
    
    print(text)
    print(cipher_text)
    print(decoded_text)
    Ответ написан
    1 комментарий
  • Как добавить задачу cron на ubuntu при помощи скрипта?

    Vindicar
    @Vindicar
    RTFM!
    Я бы не советовал трогать crontab.
    Есть же cron.hourly, cron.daily, и т.п., в которых можно создать свой, отдельный скрипт.
    Или есть cron.d, в котором каждый файл интерпретируется как мини-crontab.
    Ответ написан
    3 комментария
  • Почему не работает код python для телеграмм бота?

    Vindicar
    @Vindicar
    RTFM!
    У тебя, судя по скрину, используется виртуальное окружение проекта (.venv).
    Ты в него ставил либу, или в глобальный интерпретатор?
    Ответ написан
    Комментировать
  • Как запустить одну и ту же функцию с разными параметрами одновременно?

    Vindicar
    @Vindicar
    RTFM!
    Всё проще.
    У тебя во второй и третьей строке у start() скобки пропущены.

    Вот только цикл while там ну очень не очень. Хотя бы задержку секунд 20 поставь, а то нарвёшься на бан на сайте, или просто подвесишь комп диким числом потоков.
    Ответ написан
    Комментировать
  • Как работать с календарём в Python?

    Vindicar
    @Vindicar
    RTFM!
    Как правильно посоветовали в комментариях, разбей задачу на меньшие.
    1. Как получить данные с сайта университета? Смотри в сторону пакета requests.
    Также тебе потребуется понять, как сам сайт выкладывает эти данные. Просто сохрани скачанную страницу в файл и посмотри, есть ли внутри данные, которые тебе нужны. Вообще советую сохзранять страницу в файл при любой ошибке, чтобы понять, не послал ли тебя сервер на крайний север. =)

    Если данные записаны прямо в страницу, можешь использовать, скажем, BeautifulSoup, чтобы выцарапать их из HTML разметки.
    Если же данных в странице нет, значит, сайт делает отдельный XHR запрос для подгрузки данных. Открой страницу расписания, и на этой вкладке открой панель разработчика в бразуере (обычно F12), а затем обнови страницу. Посмотри на запросы (там может быть фильтр по XHR, используй его) и найди тот, который подгружает что-то, похожее на нужные данные.
    Тут могут быть два варианта: подгружается HTML разметка - тогда снова смотри в сторону BeautifulSoup. Или подгружается JSON - тогда используй встроенный в питон модуль json.
    2. Так или иначе, ты должен будешь построить структуру данных, которая хранит требуемую тебе инфу. Например, список из словарей, где каждый словарь описывает одну пару. Структуру строй так, чтобы тебе с ней было удобно работать. Анализируя полученные в п. 1 данные, заполни эту структуру.
    3. Выбери, с каким календарём ты всё-таки работаешь, и погугли, есть ли у него API. Привыкай искать и читать оригинальную документацию! Чатгпт и прочее может врать или ссылаться на устаревшие сведения.
    Скорее всего API будет сводиться к HTTP запросам, которые можно будет выполнить с помощью всё того же requests.
    Ответ написан