Ответы пользователя по тегу Python
  • Как в Python проверять, в каких скриптах какие ресурсы, импортируемые из других скриптов, используются?

    Vindicar
    @Vindicar
    RTFM!
    Ну вот тут ты столкнулся с проблемой изоляции изменений.
    Обозначим код, который предоставляет какую-то услугу, сервисом, а код, который эту услугу использует - клиентом.
    Тогда есть два типа изменений: изменение реализации сервиса и изменение интерфейса сервиса.

    Первое изменение затрагивает сервис, но необязательно затрагивает клиента. Если клиент обращается к сервису таким же образом, получает результат в таком же виде, получает сведения об ошибках таким же способом, то с точки зрения клиента изменения не было. Пусть даже в реальности сервис переписали с нуля.
    Пример: пусть у нас есть функция сортировки списка "на месте", т.е. изменяя этот список.
    def sort_list(original: list):
    Если раньше она сортировала список пузырьком, а потом мы её переписали на quicksort - с точки зрения кода, который эту функцию использует, ничего не изменится, кроме времени работы функции. Клиент все равно будет использовать функцию так:
    some_list = [ ... ]
    sort_list(some_list)


    Второй тип изменений меняет интерфейс сервиса, т.е. то, как он взаимодействует с клиентом.
    Продолжая пример, если мы решили, что теперь функция будет возвращать отсортированную копию списка, не трогая исходный список:
    def sort_list(original: list) -> list:
    Это изменение сломает клиентский код, так как ему теперь придётся использовать функцию так:
    some_list = [ ... ]
    some_list = sort_list(some_list)

    Т.е. наше изменение сломало обратную совместимость: старый код не может работать с новыми версиями.

    Если быть осторожным, то можно внести изменение в интерфейс, не сломав обратную совместимость. Например, если мы решили научить нашу функцию сортировать не только по возрастанию, но и по убыванию:
    def sort_list(original: list, inverse: bool = False):

    Тогда изначальный способ вызова sort_list(some_list) по прежнему будет работать. Мы расширили старый интерфейс, а не заменили его.

    Так вот, к чему я всё это говорю: умные IDE, вроде PyCharm, могут находить случаи использования того или иного элемента в рамках одного проекта (!). Но эта возможность очень ограничена, так как всегда можно так запутать код, что никакая IDE не разберётся.
    Поэтому лучше заранее продумывать, как клиентский код будет обращаться к услугам твоих сервисов, чтобы не пришлось переписывать интерфейс, а только реализацию.
    Ответ написан
    Комментировать
  • Бот рандомно выбирает вопрос, но проверяет по ответам для 1 вопроса, как исправить?

    Vindicar
    @Vindicar
    RTFM!
    if x == 1:
        @bot.message_handler(content_types="text")
        def send(message):
            ...

    Так работать НЕ будет. Ты явно не очень понимаешь, что делаешь.

    1. Должна быть только одна функция, декорированная как @bot.message_handler(content_types="text"). Если их несколько, отработате только одна.

    2. Ты должен хранить сведения:
    - Какой вопрос был последним задан тому или иному пользователю
    - Какие вопросы были уже заданы тому или иному пользователю
    В рамках обучения их можно хранить в словаре, но в реальном боте потребуется постоянное хранилище (например, БД), в котором эти сведения пережили бы перезапуск бота

    3. Внутри обработчика @bot.message_handler ты должен получить ID пользователя, отправившего сообщение, взять из описанного выше хранилища номер последнего заданного вопроса (если есть), и уже на основании этого номера судить о том, правильный ли ответ.
    Ответ написан
  • Как поделиться переменными между потоками из разных модулей?

    Vindicar
    @Vindicar
    RTFM!
    Для начала скажи, какую задачу ты пытаешься решить.
    Для случая с одноразовым потоком-воркером, тебе не нужна глобальная переменная. Достаточно класса.
    import threading
    
    class MyWorkerThread(threading.Thread):
        def __init__(self, arg1: float, arg2: float): # передаём потоку входные данные 
            # поток не должен их менять!
            super().__init__()
            self.arg1 = arg1
            self.arg2 = arg2
            self.result: t.Optional[float] = None
        def run(self):
            time.sleep(10) # имитируем длительную работу
            self.result = self.arg1 + self.arg2

    И тогда ты можешь его использовать так:
    worker = MyWorkerThread(42, 69)
    worker.start()
    while True: 
        if worker.is_alive():  # проверяем, жив ли поток
            # делаешь ещё что-то, пока поток работает
            print('Still working...')
            time.sleep(0.5)
        else:
            # поток завершился, даём знать пользователю.
            print(f'Done! Result is {worker.result}!')
            break # выходим из цикла

    Если же тебе нужно просто дождаться конца потока, ничего не делая в процессе, можно сделать просто worker.join()

    Сложности начинаются, когда тебе нужно взаимодействовать с длительным потоком. Усложним пример:
    import threading, queue
    
    class MyWorkerThread(threading.Thread):
        def __init__(self, arg1: float, arg2: float): # передаём потоку входные данные 
            # поток не должен их менять!
            super().__init__()
            self.arg1 = arg1
            self.arg2 = arg2
            self.result: t.Optional[float] = None
            self.progress = queue.Queue()
        def run(self):
            for i in range(10):
                time.sleep(1) # имитируем длительную работу
                self.progress.put(i/10) # сообщаем о прогрессе
            self.result = self.arg1 + self.arg2
            self.progress.put(1.00)

    Тогда код использования изменится следующим образом:
    worker = MyWorkerThread(42, 69)
    worker.start()
    while True: 
        if worker.is_alive():  # проверяем, жив ли поток
            # делаешь ещё что-то, пока поток работает
            try:
                progress = worker.progress.get(block=True, timeout=0.5)
            except queue.Empty: # поток ничего не сообщил
                print('Still working...')
            else:
                print(f'Still working... {progress:.0%}')
                worker.progress.task_done() # один вызов task_done() на один успешный вызов get()!
        else:
            # поток завершился, даём знать пользователю.
            print(f'Done! Result is {worker.result}!')
            break # выходим из цикла

    Если тебе нужно передать потоку новые задания, то можешь использовать ещё одну queue.
    Ответ написан
    Комментировать
  • Multiprocessing.Process OSError: [WinError 87] Параметр задан неверно, что делать?

    Vindicar
    @Vindicar
    RTFM!
    Ну так тебе же говорят:
    _pickle.PicklingError: Can't pickle <function <lambda> at 0x00000122F23CEB00>: attribute lookup <lambda> on __main__ failed

    Для передачи данных в соседний процесс они сериализуются через модуль pickle. А сериализовываться так может далеко не всё.
    Если тебе нужно скинуть в соседний процесс код... это проблема. Может, получится скинуть словарь с локальными переменными, строку с кодом, и вычислить эту строку через eval() уже "на месте".
    Альтернативно, просто замени лямбду на нормальную функцию, и попробуй передать либо её, либо имя этой функции, чтобы дочерний процесс сам её взял из globals() или еще откуда.
    Ответ написан
    Комментировать
  • Не работает replace в голосовом ассистенте?

    Vindicar
    @Vindicar
    RTFM!
    Ну ты же явно какую-то глупость делаешь.
    if cmd == "sreachyoutube":
            search_term = cmd.replace("Кеша видео", " ")

    Если в переменной cmd лежит строка sreachyoutube(!), то там по определению не может содержаться подстроки "Кеша видео".
    Ответ написан
    8 комментариев
  • Как узнать находятся ли все элементы списка в другом списке?

    Vindicar
    @Vindicar
    RTFM!
    Преврати первый список и второй список в множества. Если первое множество является подмножеством (issubset()) второго, то условие выполнилось.
    Если есть возможность строить множество одновременнео с первым списком, это упростит задачу.
    Ответ написан
    1 комментарий
  • Где ошибка в коде?

    Vindicar
    @Vindicar
    RTFM!
    Ну что, ошибка правильно тебе говорит.
    def __init__(self, replaybtn):
            self.replaybtn = replaybtn

    Ты говоришь, что конструктор твоего класса Dinobot принимает один параметр, replaybtn.
    А ты ему потом передаёшь два параметра: (470, 440), (202, 442)
    Ответ написан
    2 комментария
  • Как объединить в проверке (if call.data == '') несколько параметров?

    Vindicar
    @Vindicar
    RTFM!
    if call.data == 'apple' or 'melon':    #Вот в этом месте проблема

    Ещё один ботописатель с этой проблемой. Вот откуда такое упорное желание дословно переводить с русского на питон?

    Правильно:
    if call.data in ('apple', 'melon'):
    Ну или так:
    if call.data == 'apple' or call.data == 'melon':

    То, что ты написал, расшифровывается как
    if (call.data == 'apple') or ('melon' != ''):
    что эквивалентно
    if (call.data == 'apple') or True:
    А x or True всегда даст True, так же как x and False всегда даст False.
    Так что получается в итоге
    if True:

    Учи основы языка - потом уже берись за ботов.
    Ответ написан
    1 комментарий
  • Как сделать перенос строк в парсере?

    Vindicar
    @Vindicar
    RTFM!
    Так же, как и всегда. Добавь символ "\n" в конец выводимой строки.
    Ответ написан
    2 комментария
  • Не добавляется столбец в sqlite3, что делать(срочно, пожалуйста)?

    Vindicar
    @Vindicar
    RTFM!
    Что лежит в переменной channel_id? Это строка? Если это строка, она точно содержит только символы, допустимые для имени столбца? Ты это проверил, или понадеялся на авось?

    А вообще, если ты вынужден делать ALTER TABLE на ходу, у тебя большие проблемы с архитектурой БД. Дальше будет только хуже.
    Лучше опиши задачу, которую пытаешься решить (хотя я догадываюсь, что это очередной чудо-бот).
    Ответ написан
  • Как решить пример в Python, который находится как string в переменной?

    Vindicar
    @Vindicar
    RTFM!
    Тупой способ: использовать eval(), но это очень рискованно! Если данные приходят извни, атакующий может вписать туда почти любой питоновский код. Потом не плачь, что тебя не предупреждали.

    Умный способ, на примере (-5 * 3) - 2:
    1. Разбить строку на осмысленные токены : число, оператор и т.п. Для каждого токена хранить его тип и значение. Получим список в духе:
    [('parenthesis', '('), ('operator', '-'), ('number', '5'), ('operator', '*'), ('number', '3'), ('parenthesis', ')'), ('operator', '-'), ('number', '2') ]

    Тут ушли все пробелы.
    2. Подправить унарные операторы. Операторы "+" и "-", следующие за началом строки, открывающей скобкой или другим оператором, являются унарными (с одним аргументом), а не бинарными (с двумя). Их стоит обозначить по другому, чтобы не путать. Получим такое (смотри на второй токен):
    [('parenthesis', '('), ('operator', 'unary-'), ('number', '5'), ('operator', '*'), ('number', '3'), ('parenthesis', ')'), ('operator', '-'), ('number', '2') ]

    3. Преобразовать из инфиксной нотации (2 + 3) в постфиксную (2 3 +). Для этого есть железнодорожный алгоритм Дейкстры, он гуглится. В постфиксной нотации всё решается куда проще, так как ей не нужны скобки. Наш пример запишется как 5 unary- 3 * 2 -
    [('number', '5'), ('operator', 'unary-'), ('number', '3'), ('operator', '*'),  ('number', '2'), ('operator', '-')]

    4. Решить пример. Алгоритм очень простой. У нас есть список аргументов, поначалу он пуст. Идём по списку токенов (в постфиксной нотации):
    а. Если токен - число, поместить его в конец списка аргументов, перейти к следующему токену.
    б. Если токен - унарный оператор, взять и удалить последний элемент из списка, применить к этому элементу оператор, результат добавить в конец списка.
    в. Если токен - бинарный оператор, взять и удалить последние 2 элемента из списка, применить к ним оператор (не перепутай порядок аргументов!), результат добавить в конец списка.
    Всё! Когда токены кончатся, у тебя в списке аргументов должно быть ровно одно число - это и будет ответ.
    На нашем примере:
    [('number', '5'), ('operator', 'unary-'), ('number', '3'), ('operator', '*'),  ('number', '2'), ('operator', '-')]

    1. Число "5": список аргументов [ 5 ]
    2. Унарный минус: берем 5 из списка, применяем оператор, кладем результат назад. Список аргументов [ -5 ]
    3. Число "3": список аргументов [ -5, 3 ]
    4. Оператор *: берем -5 и 3 из списка, применяем оператор, кладём результат назад. Список аргументов [ -15 ]
    5. Число "2": список аргументов [ -15, 2 ]
    6. Бинарный минус: берем -15 и 2 из списка, применяем оператор, кладём результат назад. Список аргументов [ -17 ]
    Токены закончились, в списке одно число -17. Это ответ.
    Ответ написан
    Комментировать
  • Почему при использовании with open не добавляются в сравнение отрицательные числа?

    Vindicar
    @Vindicar
    RTFM!
    Ну во-первых, почему вначале read(1)? Это читает один символ, а не одну строку, так что если у тебя более 10 чисел в файле, работать будет некорректно.
    Во-вторых, isdigit() проверяет, что строка содержит только цифры. Знак "-" цифрой не является. Поэтому твой код такие строки игнорирует.
    Ответ написан
    3 комментария
  • Как сделать такой перебор через itertools на Python?

    Vindicar
    @Vindicar
    RTFM!
    Никак. Я бы решил по-другому.
    Рассматривай твою строку как число в N-рной системе счисления.
    Например, если среди символов строки могут быть только цифры 0-9 - это будут десятичные числа.
    Если будут a-z - то получим систему счисления с основанием 26.

    Тогда каждой строке можно будет соспоставить число - номер этой строки в списке всех возможных строк. В твоём случае "1111111111" соотвествует номер 0, "1111111112" - номер 1, и так далее.

    Соответственно, перебор строк будет сводиться к перебору всех чисел от 0 до числа возможных строк, с последующим переводом в выбранную "систему счисления". А остановка и возобновление перебора - сведётся к запоминанию текущего числа и выбору другой начальной позиции в следующий раз.

    Хотя можно и попытаться пропустить N первых строк в выхлопе product(), но это будет медленно, если нужно начать где-то в конце.
    Ответ написан
    Комментировать
  • Как получить другой элемент Literal?

    Vindicar
    @Vindicar
    RTFM!
    Литерал означает, что данная переменная/параметр всегда имеет одно из перечисленных значений. Так что...
    other_player_mark = 'cross' if mark == 'zero' else 'zero'

    Не, ты, конечно можешь извратиться...
    import typing as t
    
    Mark = t.Literal['cross', 'zero']
    
    class TicTacToePlayer:
        def __init__(self,
                     mark: Mark,
                     field: TicTacToeField
                     ):
            self.cells = []
            self.mark = mark
            self.field = field
        
        def opposite(self, mark: Mark) -> Mark:
            values = t.get_args(Mark)
            idx = values.index(mark)
            next_idx = int(not idx)
            return t.cast(Mark, values[next_idx])

    но лучше не надо!
    Ответ написан
  • Как сделать, чтобы картинка не обрезалась при отправке через asyncio?

    Vindicar
    @Vindicar
    RTFM!
    recieved = await reader.read(2048)
    Ты читаешь 2КБ. И всё.
    Проблема в том, что размер пакета при передаче по сети ограничен. Хоть гигабайт отправь, приходить будет порциями. И вообще, отправитель (или промежуточное сетевое оборудование) может данные хоть по байту нарезать, ты это не контролируешь.
    Поэтому обязательно надо предусматривать какой-то способ определить конце передачи, и повторять чтение в цикле, поке конец не достигнут. Основных способов два.
    1. Передавать сначала размер данных в байтах (фиксированным размером - например, как 4байтовое целое), потом сами данные. Читаешь размер, разбираешь его в целое число, читаешь данные, пока не накопится желаемое число байт.
    2. После переданных данных передавать условную последовательность "конец передачи". Тогда читаешь данные, пока эта последовательность не встретится.
    У тебя данные бинарные, первый способ сработает лучше.
    Ответ написан
  • Как убрать скобки?

    Vindicar
    @Vindicar
    RTFM!
    1. Убрать list(). Обратиться по индексу 0 - bd[0].
    2. Почитать про списки и кортежи.
    Ответ написан
    Комментировать
  • Как запустить shell-комманду в новом процессе без блокировки потока?

    Vindicar
    @Vindicar
    RTFM!
    communicate() как раз и ждёт. Вместо этого ты можешь попробовать просто читать из proc.stdout.
    С асинхронностью сложнее, но ты можешь попробовать запустить функцию в потоке через loop.run_in_executor(). Это позволит завернуть поток в асинхронную задачу. Так как поток будет висеть в ожидании ввода-вывода, он не должен сильно влиять на GIL и мешать другим потокам.
    Ответ написан
    2 комментария
  • Бот на Telebot падает, когда отправляешь второй одинаковый запрос в БД sqllite. Как исправить?

    Vindicar
    @Vindicar
    RTFM!
    Если ты хочешь при любом действии пользователя убедиться, что он в базе, то тебе нужен не просто INSERT, а INSERT ON CONFLICT IGNORE или INSERT ON CONFLICT UPDATE. Если тебе требуется что-то обновлять (например, хранить для пользователя момент последнего обращения к боту), то нужен второй. Если не требуется, то лучше первый.
    Ответ написан
    Комментировать
  • Только начал свое знакомство с Python. Вроде бы код верный, но высвечивается ошибка. В чем может быть проблема?

    Vindicar
    @Vindicar
    RTFM!
    Во-первых, это неверно. map() принимает два аргумента: функцию, которую нужно применить, и коллекцию, к которой её применяем. Ты, к тому же, скобку не закрыл.
    a,b,n=map(float(input().split())
    Надо так:
    a,b,n=map(float, input().split() )

    Во-вторых, по-моему, у тебя кривой расчёт. ИМХО, проще всего рассчитать цену книги в копейках, а потом уже отталкиваться от этого.
    price_kop = 100 * a + b
    total_kop = price * n
    total_gr, total_kop = divmod(total_kop, 100)
    print(total_gr, total_kop)
    Ответ написан
    Комментировать
  • Как обработать закрытие консоли?

    Vindicar
    @Vindicar
    RTFM!
    Я также попробовал через signal.
    Видимо, на практике нереально. Спасибо винде.
    Ответ написан
    Комментировать