Задать вопрос
Ответы пользователя по тегу Python
  • Как заставить бота ждать ответа пользователя?

    Vindicar
    @Vindicar
    RTFM!
    Ну для начала твоя лесенка из if-elif-elif-... - плохое решение.
    Сделай нормальную структуру данных вместо пачки переменных. Используй словарь, или ещё что. Например, такого вида:
    # словарь, где ключ - строка с названием пунка отправления, а значение - ещё словарь,
    #   где ключ - строка с названием пункта назначения, а значение - список,
    #     где элементы - пары строк ("время отправления", "время прибытия")
    # Тогда с таким словарём можно будет работать так:
    bus_timetable: dict[str, dict[str, list[tuple[str, str]]]] = {
        # ты словарь заполняешь по результатам парсинга, ну и то хорошо
        # я для примера запишу прямо так
        'Владимир' : {
            'Муром': [
                ('8:00', '11:00'),
                ('9:00', '12:00'),
                ('10:00', '13:00'),
            ],
        },
    }
    point_from = 'Владимир'
    point_to = 'Муром'
    # для примера вывожу в консоль, для бота перепишешь сам
    print(f'Автобус из {point_from} в {point_to}')  
    for departure, arrival in bus_timetable[point_from][point_to]:
        print(f'Отходит в {departure}, прибывает в {arrival}')

    Тогда тебе не потребуется делать кучу веток для разных городов, достаточно лишь определить значения point_from и point_to. Более того, ты можешь просто перечислить ключи внешнего словаря при создании кнопок:
    kbd_from = types.ReplyKeyboardMarkup(resize_keyboard=True)
    buttons = [types.KeyboardButton(point_from) for point_from in bus_timetable]  # список кнопок
    kbd_from.add(*buttons)  # если надо добавить все кнопки сразу

    Похожий приём возможен и для определения кнопок для городов назначения.

    А теперь по тому, как сделать выбор. Тут есть два варианта, но давай пока поговорим о ReplyKeyboardMarkup, которую ты используешь.
    Если ты хочешь использовать ReplyKeyboardMarkup, то стоит помнить - её кнопки дают тот же эффект, как если бы пользователь просто ввёл текст кнопки сам. Поэтому тебе потребуется использовать register_next_step_handler(). Ты не привёл код своей попытки, но в целом идея несложная. В основном обработчике события ты проверяешь, что текст сообщения содержится в bus_timetable (оператор in в помощь) - это значит, что текст сообщения содержит название известного боту города.
    Тогда ты делаешь вызов register_next_step_handler() и ставишь обработчик второго сообщения. Единственное "но": тебе нужно будет обработчику передать первый выбранный город. Это будет выглядеть как-то так:
    @bot.message_handler(content_types=['text'])
    def first_message_handler(message):
        if message.text in bus_timetable:  # назвали известный город? Это пункт отправления
            kbd = ...  # тут определяешь клавиатуру, опираясь на города в bus_timetable[message.text]
            response = bot.reply_to(message, "А теперь назовите пункт назначения", reply_markup=kbd)
            # обрати внимание: все параметры после указания функции second_message_handler будут
            # переданы в эту функцию при её вызове. А вызвана она будет, когда пользователь ответит.
            bot.register_next_step_handler(response, second_message_handler, message.text)
        elif ...  # нам назвали не город - тут можно проверить другие команды
    
    def second_message_handler(message, point_from):
        # а эта функция будет вызвана только когда пользователь уже назвал пункт отправления
        # point_from будет содержать название города, отправленное пользователем.
        if message.text in bus_timetable[point_from]:  # нам назвали допустимый пункт назначения
            bus_rides = bus_timetable[point_from][message.text]  # список рейсов
            ...  # дальше перебираем список bus_rides и выводим его пользователю
        else:  # пользователь назвал что-то другое
            ...  # говорим пользователю, что он дурак
    Ответ написан
    1 комментарий
  • Как определить область в потоке видео и среагировать на обнаружение?

    Vindicar
    @Vindicar
    RTFM!
    Ну так и скажи, "я хочу написать чит для Dead By Daylight, помогите!"
    Сделай полярное преобразование нужного куска экрана. Тогда нужная полоса превратится в вертикальную, и её легко можно будет выбрать обрезкой, а индикатор будет двигаться не по кругу, а по вертикали, и его движение будет проще анализировать.
    Впрочем, приколюхи вроде докторского безумия, которые меняет положение и направление движения индикатора, собьют твой алгоритм. А ещё есть скилл-чеки, у которых просто нет идеальной зоны, только хорошая.
    Ответ написан
    2 комментария
  • Как правильно проксировать запрос при коннекте к aiosmtplib?

    Vindicar
    @Vindicar
    RTFM!
    Погоди-ка, а параметр sock на что? Он есть и у функции send(), и у конструктора SMTP(). Подготовь запроксированный экземпляр socket.socket и отдай туда.
    Судя по упомянутому в комментах issue, нужно будет самому закрыть этот сокет, когда закончишь работу с SMTP.

    Нет, конечно, есть варианты жёстче. Например, отнаследоваться от SMTP, переопределить _create_connection(), и заменить/пропатчить self.loop так, чтобы вместо self.loop.create_connection() вызывалась твоя корутина. В ней уже формировать сокет как тебе надо.
    Но мне кажется, проще поработать с sock.
    Ответ написан
    Комментировать
  • Возможно ли создать асинхронную функцию чтобы отправлять в неё данные постепенно без await?

    Vindicar
    @Vindicar
    RTFM!
    asyncio.Queue в помощь. Чтобы синхронно помещать туда элементы, или используй put_nowait(), или выкручивайся с вызовом put() через create_task().

    Но у меня прямой вопрос: а как ты, собственно, собираешься выполнять синхронную g() в асинхронной программе? Потому что если через потоки, то про совет выше можешь забыть.
    Ответ написан
  • Почему Telegram бот не отвечает?

    Vindicar
    @Vindicar
    RTFM!
    Ну тебе же английским по белому написано:
    RuntimeError: This Application was not initialized via `Application.initialize`!

    Нужно перед вызовом application.start() добавить аналогичный вызов initialize().

    И да, ты пытаешься трогать элементы управления своего окна из другого потока. Так нельзя.
    Ответ написан
    Комментировать
  • Как в numpy максимально быстро изменить структуру данных в массиве?

    Vindicar
    @Vindicar
    RTFM!
    CUDA тут не нужна, просто не следует перебирать массив numpy питоновскими циклами.
    Если есть возможность - выражай желаемое через операции над массивом в целом. Тогда цикл организует numpy, и он это делает через нативный код, ЕМНИП - работает в разы быстрее.
    Подход ниже сожрёт больше памяти, но зато короткий, выразительный и использует только средства numpy.
    image = cv2.imread("image.png")
    image = image.astype(numpy.uint32)  # смена типа нужна, иначе при сдвиге потеряются разряды
    image_clone = (image[..., 0] << 16) | (image[..., 1] << 8) | image[..., 2]

    Если хочется заморочиться по памяти:
    image = cv2.imread("image.png")
    image_clone = numpy.zeros(image.shape[:2], numpy.uint32)
    # краткие формы операций не создают новый массив, а изменяют существующий по месту
    image_clone |= image[..., 0]
    image_clone <<= 8
    image_clone |= image[..., 1]
    image_clone <<= 8
    image_clone |= image[..., 2]


    P.S.: как там, у imread() починили уже неумение работать с юникодными путями?
    Ответ написан
    Комментировать
  • Бот для уведомлений с ютуба не работает больше 3х дней, где я мог ошибиться?

    Vindicar
    @Vindicar
    RTFM!
    Во-первых, научись регистрировать проблемы. Почитай про logging, как его настраивать. Расставь в боте и в парсере try-except и записывай в лог возникающие ошибки. Тогда у тебя будет текст ошибки, с которым уже можно как-то работать.

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

    Тут вопрос такой: а нужна ли вообще тут БД? Если да, то для чего она нужна?
    yt-dlp, к сожалению, синхронный - но ты ведь можешь выполнять этот код через run_in_executor(). Тогда получится интегрировать парсер прямо в бот, и отправлять сообщения немедленно.

    Альтернативно, можно схитрить так: сделать боту учётку на ютубе, подписать его на нужные каналы и настроить уведомления о новых видео по почте, а в боте мониторить почтовый ящик через email-monitor или подобное.
    Ответ написан
    Комментировать
  • Зачем вообще нужна библиотека python turtle в реальной практике?

    Vindicar
    @Vindicar
    RTFM!
    Обучение. Многие языки для школьников основаны на принципе "черепашки". Сам начинал с LogoWriter и L-Graph, но есть и другие.
    А тут сразу можно привыкнуть к синтаксису питона, заодно.
    Ответ написан
    Комментировать
  • Почему не получается закрыть окно в PyQt5?

    Vindicar
    @Vindicar
    RTFM!
    Ну у твоего класса действительно есть только метод setupUi(), и нет метода close(). Неудивительно, что Питон этот метод тоже не находит.
    Я с Qt не знаком, поэтому поинтересуюсь: а MyMindow точно должно наследоваться от object? Может, от какого другого класса?

    А вообще выглядит всё так, словно этому коду место в MainWindow.
    Ответ написан
    Комментировать
  • Как получить данные с сайта росреестра?

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

    В данном случае, и requests и браузер получают в ответе вот это:
    <body style="height: 100%; width:100%;">
    <div id=app style="height: 100%; width:100%;"></div>
    <script src=js/chunk-vendors.c1d200da.js></script>
    <script src=js/chunk-common.adcda62e.js></script>
    <script src=js/chunk-esri.caf28189.js></script>
    <script src=js/index.ca134891.js></script>
    </body>

    Как видишь, нужного элемента тут просто нет, потому что он создаётся динамически, через JavaScript. А BS не выполняет скрипты.

    Варианта только два:
    1. Выяснять, каким запросом сайт подтягивает данные для этого, научиться этот запрос делать самостоятельно и вытаскивать искомые данные из полученного. Медленно и трудно на подготовительном этапе, зато потом работает сравнительно быстро... если, конечно, сайт защиту от парсинга не поставит.
    2. Запускать управляемый браузер, дать ему загрузить страницу и выполнить скрипты, а потом уже искать нужные данные. Быстрое решение в плане разработки, но более медленное и ресурсоёмкое в дальнейшем использовании.
    Ответ написан
    Комментировать
  • Как отрыть ссылку с помощью flet с регистром?

    Vindicar
    @Vindicar
    RTFM!
    Твоя ссылка парсится так:
    1. nullsbrawl - схема
    2. createAndJoinRoom - адрес узла, точнее, доменное имя. Доменные имена не имеют регистра, насколько я знаю, т.е. habr.com и HABR.COM - одно и то же имя. Вероятно, поэтому оно и преобразуется в нижний регистр.
    3. пустой путь к ресурсу - подразумевается корневой ресурс
    4. всё, что после ? - параметры запроса

    Ты можешь попробовать изменить ссылку так: nullsbrawl:///createAndJoinRoom?....
    Обрати внимание на дополнительный слэш. Эта ссылка парсится так:
    1. nullsbrawl - схема
    2. адрес узла пустой
    3. createAndJoinRoom - путь к ресурсу на указанном узле. Пути регистрозависимы в URL, а потому его трогать не будут.
    4. всё, что после ? - параметры запроса
    Ответ написан
  • Почему питоновский скрипт сам по себе рабочий, а в виде службы - нет?

    Vindicar
    @Vindicar
    RTFM!
    Виртуальное окружение в проекте есть? Если да, то запускать скрипт надо не системным питоном, а питоном из этого окружения.
    EDIT1:
    Если из командной строки дать pyhon3 /home/lxm213/script.py, то скрипт работает штатно и без ошибок

    Т.е. скрипт запускаешь в системном окружении, а не в виртуальном. ОК, тогда по идее он должен работать...
    Но встаёт вопрос: как ты ставил schedule? Просто pip install schedule? Из-под рута ставил или с правами просто пользователя?
    Если ставил из-под рута, то библиотека будет установлена в системный интерпретатор и будет доступна всем пользователям.
    Если ставил не из-под рута, то, возможно, pip поставил её куда-то в твой домашний каталог - я знаю, что под виндой он так делает. Тогда библиотека доступна только если запускать скрипт из-под твоего пользователя.

    Отсюда три возможных решения:
    1. Создать виртуальное окружение для проекта, поставить библиотеку в это виртуальное окружение (используя местный pip), запускать скрипт через python3 из этого окружения.
    2. Если библиотека была поставлена без рут-прав, указать в .service-файле, что скрипт надо запускать от имени твоего пользователя, а не от имени root.
    3. Убедиться, что библиотека поставлена в системный интерпретатор. При необходимости поставить её с рут-правами. Тогда скрипт будет работать из-под любого пользователя.
    Ответ написан
    9 комментариев
  • Обработка params ?fields в апи запросе для вывода в response?

    Vindicar
    @Vindicar
    RTFM!
    Рекурсивно делай. Опиши требуемую функцию:
    def filter_fields(obj: dict[str, ...], fields: list[str]) -> dict[str, ...]:
        """obj - фильтруемый словарь, fields - множество полей, которые надо оставить."""
        ...

    Проблема в том, что поля могут быть вложенным. Тогда первым делом составляешь набор нужных тебе полей верхнего уровня:
    top_level_fields = {}
    for f in fields:
        top, _, bottom = f.partition('.')
        top_level_fields.setdefault(top, [])
        if bottom is not None:
            top_level_fields[top].append(bottom)

    Для
    fields=['boardId', 'name', 'Data.Matches.t.id', 'Data.Matches.m']
    в top_level_fields получишь
    { 'boardId': [], 'name': [], 'Data': ['Matches.t.id', 'Matches.m']}

    Дальше перебираешь поля, собираешь их значения в один мешок и смотришь, есть ли для них нижележащие поля:
    output = {}
    for top, bottom in top_level_fields.items():
        if bottom:
            output[top] = filter_fields(obj[top], bottom)  # что делать, если ключа top нет?
        else:
            output[top] = obj[top]

    Таким образом наберёшь всё, что надо.
    Ответ написан
    Комментировать
  • Как получить содержимое сообщения до его редактирования в discord через requests?

    Vindicar
    @Vindicar
    RTFM!
    Насколько я знаю - никак. Ты можешь ловить событие редактирования сообщения - тогда ты получишь старую и новую версии. Но и только.
    Ответ написан
  • Взаимодействие методов внутри класса?

    Vindicar
    @Vindicar
    RTFM!
    # Ты заявляешь следущее:
    # "метод udar должен вызываться на экземпляре объекта (self) с двумя параметрами: hp и dmg"
    def udar(self,hp,dmg):
        print("Наносит урон с руки")
        # при этом метод не использует переданные параметры?
        rep = self.hp - self.dmg  # почему self.dmg? Боец бьёт себя?
        return "Осталось здоровья: " + rep
    
    
    # а ниже у тебя:
    # метод udar() фактически вызывается с одним параметром: другим бойцом
    boec.udar(boec2)


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

    Вот моя идея (может, несколько усложнённая, но, надеюсь, понятная):
    class Fighter:
        def __init__(self, name: str, hp: int, damage: int):  # указывай типы параметров - так проще понять, что нужно методу
            """Конструктор инициализирует экземпляр бойца заданными значениями."""
            self.name: str = name
            self.hp: int = hp
            self.damage: int = damage
        
        def is_defeated(self) -> bool:
            """Возвращает True, если боец не может более сражаться."""
            return self.hp <= 0  # в простейшем случае, если у бойца не осталось HP
    
        def calculate_attack_on(self, other: 'Fighter') -> int:
            """Метод определяет, сколько урона наш боец (self) нанесёт бойцу other. Возвращает число очков урона."""
            # сейчас урон всегда одинаков, но потом тут можно будет прикрутить что-то посложнее
            # например, рандомный урон в диапазоне, или бонусы/малусы против конкретных бойцов, или ещё что
            return self.damage  
    
        def receive_damage(self, damage: int) -> int:
            """Метод определяет, сколько HP наш боец (self) потеряет, получив урон damage, и уменьшает его здоровье. Возвращает число фактически потерянных очков здоровья."""
            # сейчас метод просто вычитает урон из очков здоровья. 
            # но потом при желании можно добавить, например, механику "последний шанс", 
            # когда первый "смертельный" удар оставляет бойца на 1 хп. Или сопротивление/уязвимость к урону, или ещё что.
            lost = min(self.hp, damage)  # если у нас 1 очко здоровья, мы не можем потерять 10
            self.hp -= lost
            return lost
    
        def attack_other(self, target: 'Fighter') -> int:
            """Проводит атаку нашего бойца (self) по другому бойцу (target). Возвращает нанесённый урон."""
            attack_damage = self.calculate_attack_on(target)
            actual_damage = target.receive_damage(attack_damage)
            return actual_damage

    Как видишь, у каждого метода есть свой достаточно чётко очерченный круг обязанностей. Хотя, конечно, можно придраться и сказать, что receive_damage() делает две вещи и его стоит разбить на два метода.
    Названия методов должны однозначно указывать, что они делают. Например, udar ничего не говорит о том, кто кого ударяет, тогда как attack_other намекает, что атаку производит тот, у кого вызвали метод, в адрес того, кого передали как параметр метода. То же самое касается параметров методов.
    Весь вывод вынесен из класса, это не его обязанность:
    boec = Fighter("вася",200,22)
    boec2 = Fighter("bob",100,19)
    print(f'{boec.name}: {boec.hp} HP;  {boec2.name}: {boec2.hp} HP')
    dmg = boec.attach_other(boec2)
    print(f'{boec.name} нанёс {dmg} урона {boec2.name}')
    print(f'{boec.name}: {boec.hp} HP;  {boec2.name}: {boec2.hp} HP')
    Ответ написан
    2 комментария
  • Как ускорить запуск .exe файла?

    Vindicar
    @Vindicar
    RTFM!
    Если проблема именно в "юзер сбит с толку", то её можно обойти, а не решить. Я, например, когда писал гуишную программу, завязанную на numpy, scipy и matplotlib, в итоге плюнул и сделал простой splash screen, который показывается при загрузке и пред-импортирует требуемые модули, показывая прогрессбар. А у pyinstaller есть опция --splash, которая будет показывать прогресс распаковки.

    А так - профилируй приложение, выясняй, на чём задержка. Добавь отладочный вывод в самое начало скрипта, до импортов, чтобы понять, идёт ли задержка на этапе запуска интерпретатора, или на этапе ипорта модулей.
    Ответ написан
    Комментировать
  • Как прослушать несколько портов в python?

    Vindicar
    @Vindicar
    RTFM!
    Можно, открыв несколько сокетов и проверяя их все.
    Но начиная с определенного числа команд проще будет использовать SSH или иную форму удалённого управления.
    Ответ написан
    Комментировать
  • Как отсортировать элементы с определенным id в mongodb (pymongo) и записать их в список?

    Vindicar
    @Vindicar
    RTFM!
    В первом случае ты ищешь все элементы, у которых server_id НЕ совпадает (!=) с message.guild.id.
    Во втором случае ты ищешь все элементы, у которых server_id совпадает с message.guild.id.

    Исходя из твоего результата, я бы сказал, что у тебя в базе нет элемента с таким id. Ты уверен, что он там есть? Что насчёт типов данных - нет ли тут косяка, вроде "храню id как строку, а передаю при поиске как целое число"?

    И да, говори правильно. То, что ты делаешь - поиск/фильтрация, а не сортировка.
    Ответ написан
  • Как обрабатывать ошибки в асинхронном коде?

    Vindicar
    @Vindicar
    RTFM!
    try: 
        loop.create_task(exc())
    except ZeroDivisionError as ex:
        print(f"Ошибка {ex} обработана")

    1. Ты создаёшь задачу на базе корутины exc(). Созданная задача не выполнится немедленно, а только встанет в очередь исполнения (хотя в питоне 3.12 это поведение можно изменить, но по умолчанию это так). При этом с корутиной ассоциируется future-объект, который находится в состоянии "ожидание", так как корутина ещё не завершила работу.
    2. Ты проверяешь, не возникло ли исключение в процессе создания задачи. Это может произойти, только если exc() - не корутина. В остальных случаях операция будет успешна независимо от содержания exc(), так как см. пункт 1.
    3. Ты вызываешь loop.run_forever(). Рабочий цикл (loop) смотрит в очередь исполнения, и видит в нём только корутину exc(). Она получает управление.
    4. Корутина exc() выбрасывает исключение, но не ловит его. Ассоциированный с корутиной future-объект переходит из состояния "ожидание" в состояние "отказ", и сохраняет информацию об исключении. exc() завершает работу.
    5. Рабочий цикл проверяет, кто хранит ссылку на task - и понимает, что никто. Как следствие, даже в будущем никто не сможет узнать, что корутина выкинула исключение. Поскольку рабочий цикл - штука типовая, он понятия не имеет, что делала наша корутина и как надо реагировать на исключение в ней. А потому единственный вариант для него - написать в журнал работы о непойманном исключении и надеяться, что программист это увидит и поправит.
    task = asyncio.create_task(exc())
    try:
        # await asyncio.gather(task)  # <- gather() не нужно, если у тебя одна задача
        await task
    except ZeroDivisionError as ex:
        print(f"Ошибка {ex} обработана")

    1. Ты создаёшь задачу (успешно), а потом с помощью await-вызова просишь дождаться её завершения и получить результат.
    2. Корутина main() приостанавливает своё выполнение и сохраняет свой контекст, а также встаёт в очередь, ожидая, когда сработает future-объект, связанный с task.
    3. Рабочий цикл (loop) asyncio смотрит в очередь выполнения и видит, что корутина exc() готова выполняться (она не находится в await вызове, а только начала работу).
    4. Корутина exc() получает управление, выполняется, и генерирует исключение, которое не поймано внутри этой корутины. Future-объект, связанный с этой корутиной, переходит из состояния "ожидание" в состояние "отказ", и сохраняет информацию об исключении. Корутина exc() завершает выполнение. При этом о её future-объекте знает корутина main(), поэтому рабочий цикл не дёргается по этому поводу - у main() будет возможность отреагировать на происходящее.
    5. Рабочий цикл (loop) смотрит в очередь выполнения и видит, что там только корутина main(), причём она готова выполняться - future-объект, который она ждёт, более не находится в состоянии ожидания.
    6. Корутина main() получает управление и восстанавливает свой контекст, продолжая с того места, где она остановилась. Так как future-объект находится в состоянии "отказ", оператор await читает из него информацию об исключении и перевыбрасывает это исключение внутри main().
    7. Это исключение обрабатывается блоком try-except в main() как обычно.
    Ответ написан
    Комментировать
  • Как отправлять данные пользователю из бота telegram?

    Vindicar
    @Vindicar
    RTFM!
    У тебя, походу, button() прописан как обработчик для всех кнопок. А вот внутри него не прописана ветка для data == 'send_purchase'. На текущем шаге добавь во ВСЕ цепочки if ... elif ... elif ... ветку else, которая выводит отладочное сообщение - тогда хоть будет понятно, какой обработчик вызвался и с каким значением.
    Ответ написан
    1 комментарий