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

    Vindicar
    @Vindicar
    RTFM!
    Клавиатура обычно прикреплена к сообщению. Для её смены проще всего отправить поясняющее сообщение с новой клавиатурой.
    Ответ написан
    Комментировать
  • Как проверить количество цифер после точки во float в Python?

    Vindicar
    @Vindicar
    RTFM!
    Число цифр - штука коварная. Вот беру я Питон и вбиваю:
    >>> 0.1 + 0.2
    0.30000000000000004

    Сколько цифр должен вывести код?
    Ответ написан
    Комментировать
  • Насколько такой метод проверки является корректным и как его можно сократить?

    Vindicar
    @Vindicar
    RTFM!
    1. Не надо проверять в рантайме type(m) == int. Достаточно указать в заголовке функции
    def add_time(self, h: int = 0, m: int = 0, d: int = 0, dw: int = 0, mh: int = 0, y: int = 0):

    Этого будет достаточно, чтобы IDE/статический анализатор сказал программисту "эй, ты фигню передаёшь в этот метод". Да, это не будет контролироваться во время выполнения, но тут уже другой вопрос: если клиент-программист сделал класс, неотличимый от int, стоит ему позволить этот класс передать вместо int. Ответственность будет на нём.

    2. Ну для конкретно твоей проверки по значению можно просто:
    if any(v < 0 for v in [h,m,d,dw,mh,y]):
        raise ValueError()

    Если хочешь красиво, можно и выпендриться:
    args = {'h':h, 'm':m, 'd':d, 'dw':dw, 'mh':mh, 'y':y}
    bad = list(filter(lambda k: args[k] < 0, args.keys()))
    if bad:
        raise ValueError('Invalid values for: ' + ','.join(bad))


    Ну а при большом желании можно и целый велосипед для декларативной проверки параметров замутить.
    Но смысл?
    import functools
    import inspect
    
    # пусть чек-функция имеет вид (value) -> bool, и возвращает True для "хороших" значений. Пример:
    def not_whitespace(s: str) -> bool:
        'String must not contain only whitespace'  # док строка будет использоваться в сообщении об ошибке
        return bool(s.strip())  # проверяем что строка не состоит из одних пробелов.
    # чек-функции можно генерировать и на ходу:
    def in_range(low, high):
        def check(value):
            return (low is None or low <= value) and (high is None or value <= high)
        check.__doc__ = f'Value must be between {low} and {high}.'
        return check
    # теперь сделаем декоратор, который умеет принимать чек-функции и применять их перед вызовом цели
    def check(**checks):
        def wrapper(func):
            sign = inspect.signature(func)
            names = list(sign.parameters.keys())  # имена параметров по порядку
            not_found = set(checks.keys()) - set(names)  # все ли чеки ссылаются на известные параметры?
            if not_found:
                # у нас есть чек на неизвестный параметр!
                raise NameError(', '.join(not_found))
            # всё ок, делаем обёртку над функцией
            
            @functools.wraps(func)
            def wrapped(*args, **kwargs):
                bad = []
                for param_name, check_func in checks.items():
                    idx = names.index(param_name)
                    if idx < len(args):
                        # параметр был передан через args
                        value = args[idx]
                        if not check_func(value):  # вызываем чек-функцию
                            err = getattr(check_func, '__doc__', '')
                            if err:
                                bad.append(f'{param_name} ({err})')
                            else:
                                bad.append(param_name)
                    else:
                        pass  # могут быть хитрости с kwargs-only параметрами. Тут уж извини, мне влом писать.
                if bad:  # нашли ошибки?
                    raise ValueError('Bad value for parameters: '+', '.join(bad))
                    # тут ещё можно помудрить над скрытием последнего фрейма в traceback, но мне опять влом
                else:  # не нашли, вызываем функцию
                    return func(*args, **kwargs)
            
            return wrapped
        return wrapper
    
    # пример использования
    # строка должна быть не из одних пробелов
    # число должно быть в пределах от 1 до 10 включительно
    @check(s=not_whitespace, n=in_range(1, 10))
    def repeat(s: str, n: int) -> str:
        return s * n
    
    print(repeat('test ', 3))
    
    try:
        print(repeat('test ', 20))
    except ValueError as err:
        print('yep! it failed!', err)
    
    try:
        print(repeat('test ', -1))
    except ValueError as err:
        print('yep! it failed!', err)
    
    try:
        print(repeat('    ', 5))
    except ValueError as err:
        print('yep! it failed!', err)
    
    try:
        print(repeat('    ', 15))
    except ValueError as err:
        print('yep! it failed!', err)
    Ответ написан
    3 комментария
  • Как получить все 3 канала изображения?

    Vindicar
    @Vindicar
    RTFM!
    По умолчанию каналы изображения в opencv идут в порядке blue, green, red.
    Так что 0 - это blue, ну и так далее.
    Ответ написан
  • Бот перестаёт отвечать после возврата в главное меню, как исправить?

    Vindicar
    @Vindicar
    RTFM!
    button1 = types.KeyboardButton(" Поздороваться")
    button2 = types.KeyboardButton("❓ Задать вопрос")

    Сравни это с реализацией команды /start. У тебя тут посторонние пробелы, а сравнение строк требует ТОЧНОГО совпадения. В твоём коде - вплоть до регистра букв.
    Ответ написан
    Комментировать
  • Как понять работу следующего кода по перегрузке операторов?

    Vindicar
    @Vindicar
    RTFM!
    Потому что питон пытается "извернуться", если описаны не все операторы.
    Если выполняется a < b, но не описан оператор a.__lt__(), питон попробует вызвать b.__gt__().
    Аналогично, если не описан __ne__() а только __eq__(), и выполняется a != b, питон сделает not (a == b)
    Кое что есть тут
    Также советую глянуть в сторону functools.total_ordering.
    Ответ написан
    3 комментария
  • Для чего нужна черепашья графика?

    Vindicar
    @Vindicar
    RTFM!
    Для обучения основам составления алгоритмов сойдёт. Больше, считай, ни для чего.
    Ответ написан
    Комментировать
  • Как сделать что б while работал пока может?

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

    Vindicar
    @Vindicar
    RTFM!
    if current_time == '12:00':
    У тебя значение current_time определяется один раз, ДО цикла. Ты ожидаешь, что оно волшебным образом будет следовать за текущим временем?
    Ответ написан
    Комментировать
  • Почему проходят первые тесты, но не проходят вторые на Codewars?

    Vindicar
    @Vindicar
    RTFM!
    Тормозит, потому что ты брутфорсишь перебор простых чисел. Используй решето Эратосфена.
    Иными словами, тебе не нужно перебирать все числа меньшие N, чтобы найти делители - достаточно проверить все простые числа, меньшие или равные N/2. А так как ты находишь простые числа по возрастанию, то все эти числа ты уже знаешь.
    До кучи, 2 всегда простое, а вот другие чётные числа - нет. Можешь добавить 2 в список заранее, а цикл по i делать от 3 с шагом 2.

    Ну и да, не называй переменную list. Это сбивает с толку.
    Ответ написан
    Комментировать
  • Как из текстового файла достать строки в определённом диапазоне?

    Vindicar
    @Vindicar
    RTFM!
    Пропустить нужное число строк, прочитать нужное число строк.
    def read_lines_in_range(f, ifrom: int, ito: int) -> list[str]:
        f.seek(0)
        for i in range(ifrom):
            f.readline()
        return [f.readline() for i in range(ito-ifrom)]

    Единственное, позиция чтения в файле должна быть в начале файла. Так что файл надо либо закрыть и открыть, либо сделать f.seek(0).
    Ответ написан
    Комментировать
  • Как найти ближайшее меньшее к моему числу из заданных чисел?

    Vindicar
    @Vindicar
    RTFM!
    Так как у тебя степени двойки, то можно рассчитать математически.
    import math 
    z = float(input('Z = '))  # например, 10
    logz = math.log(z, 2)  # 10 находится между 2**3 и 2**4, так что logz будет 3 с копейками (но меньше 4)
    pwr = int(logz)  # отбрасываем дробную часть, получаем 3
    value = 2 ** pwr  # ближайшее меньшее значение - 2 ** 3

    Но для произвольных чисел это не сработает.
    Ответ написан
    Комментировать
  • Как сделать так, чтобы бот отвечал на команду start?

    Vindicar
    @Vindicar
    RTFM!
    Что за @bot.send_message? Здесь декоратору не место, тут нужен просто вызов функции.
    Ответ написан
    7 комментариев
  • Как распарсить строку (python)?

    Vindicar
    @Vindicar
    RTFM!
    shlex.split(), например?
    Ответ написан
    Комментировать
  • Как скачать музыку с Vk с помощю Python?

    Vindicar
    @Vindicar
    RTFM!
    youtube-dl?
    Оно не только YouTube умеет, а ещё много какие сайты. Есть командная строка, вроде был и питоновский интерфейс. Хотя можно тупо через subprocess запустить.
    Ответ написан
    Комментировать
  • Как записать файл в папке?

    Vindicar
    @Vindicar
    RTFM!
    Ну тебе тут и насоветовали... Читай что такое относительный и абсолютный пути.
    C:\папка\user_file - это абсолютный путь.
    \папка\user_file - это относительный путь от корня текущего диска.
    папка\user_file - это относительный путь от текущей директории.
    Текущая директория (равно как и диск) может быть разной при запуске скрипта, может быть изменена в ходе работы скрипта, и не обязательно совпадает с директорией скрипта.
    Если тебя это устраивает, например, если ты пишешь программу для командной строки, которая принимает путь как параметр - то можешь использовать относительный путь.
    Если же ты хочешь обратиться к какому-то файлу с данными в каталоге скрипта - лучше сконструировать абсолютный путь. Не указать его явно (тогда придётся менять при перемещении скрипта в другую папку), а сконструировать. Например, так:
    import pathlib  # стандартный модуль питона. Очень советую его освоить!
    import sys
    # путь к папке скрипта, например, C:\myscript
    script_dir = pathlib.Path(sys.argv[0]).parent  
    # путь к файлу собирается из частей вот так
    user_file = script_dir / "some_folder" / "user_file"  # C:\myscript\some_folder\user_file
    # обрати внимание, мы не паримся по поводу того, что 
    # под виндой разделитель каталогов \, а под линуксом /
    # это уже забота pathlib
    with user_file.open('w+') as f:
        pass  # делаешь что тебе нужно с файлом
    # через функцию open() тоже сработает, метод open() просто для удобства
    with open(user_file, 'w+') as f:
        pass  # делаешь что тебе нужно с файлом

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

    Vindicar
    @Vindicar
    RTFM!
    Регулярное выражение вида
    GotoIfTime\((?P<from>\d+:\d+)-(?P<to>\d+:\d+),(?P<days>[a-z-]+),\*,\*\?open,s,1\)
    извлечёт из строки нужные части. Их можно будет вытащить так:
    regexp = re.compile(r'GotoIfTime\((?P<from>\d+:\d+)-(?P<to>\d+:\d+),(?P<days>[a-z-]+),\*,\*\?open,s,1\)', re.I)
    m = regexp.match('GotoIfTime(09:00-17:59,mon-fri,*,*?open,s,1)')
    if m:
        print(m.group('days'), m.group('from'), m.group('to'))

    Для работы с датами datetime ну и просто работа со строками. C днём недели можно будет справиться как-то так:
    now = datetime.datetime.now()
    weekday = now.weekday()
    days = m.group('days').lower()
    days_of_week = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']  # datetime.datetime.weekday()
    if '-' in days:  # диапазон
        dfrom, _, dto = days.partition('-')
        dfrom, dto = days_of_week.index(dfrom), days_of_week.index(dto)
        if dfrom <= dto:  # обычный кейс, вроде mon-fri
            day_is_good = dfrom <= weekday <= dto
        else:  # на случае если нужно зацикливание вида fri-tue
            day_is_good = (weekday >= dfrom) or (weekday <= dto)
    else:  # один день, не диапазон
        dow = days_of_week.index(days)
        day_is_good = weekday == dow
    print(day_is_good)  # True или False


    С временем тоже несложно, разбиваешь через partition() по двоеточию, преобразуешь в целое, потом делаешь datetime.datetime.now().replace(hour=h, minute=m) и получаешь указанное время в текущий день.

    Таким образом у тебя будет два момента времени, один на базе from другой на базе to. Их можно просто сравнить с текущим: if from <= now <= to:
    Ответ написан
  • Как добавить кнопки в бота, через телеграм в Aiogram?

    Vindicar
    @Vindicar
    RTFM!
    Гипотетически можно, но практически тяжело. Объясню почему.

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

    2. Ты можешь загрузить в бота код, реализующий новую функциональность. Для этого нужно будет:
    - разбить бота на изолированные компоненты, по типу Cogs из библиотеки discord.py.
    - предусмотреть механизм подгрузки файла с компонентом в бота "на ходу".
    - предусмотреть механизм выгрузки компонента из бота "на ходу", что намного сложнее. Это потребуется, если ты захочешь заменить компонент обновлённой версией, не перезапуская бота.
    - предусмотреть механизм, позволяющий тебе отдать боту файл с кодом, чтобы тот поместил этот файл к остальным компонентам.
    Это всё нетривиальные задачи, которые требуют очень хорошего понимания как Питона, так и устройства библиотеки aiogram. Собственно, беглый взгляд на доки показывает, что aiogram вообще не предусматривает удаления обработчиков событий. А это значит что единственный способ выгрузить функциональность из бота - это его перезапуск.
    Ответ написан
    Комментировать
  • Как сделать вывод определённого количества столбцов?

    Vindicar
    @Vindicar
    RTFM!
    Читай про операторы LIMIT и OFFSET у SQL-запроса.
    Если размер страницы N записей, то для выдачи страницы i тебе нужно будет задать OFFSET (i - 1) * N LIMIT N.
    Разумеется, нужно также задать порядок сортировки записей через ORDER BY.
    Ответ написан
    Комментировать
  • Как создать клавиши с названиями из списка?

    Vindicar
    @Vindicar
    RTFM!
    async def choice_cat(message):
        CATs = cur.execute(f'SELECT name FROM cathlete WHERE id = "{message.from_user.id}"').fetchall()
        for fds in range(len(CATs)):
            astt = ReplyKeyboardMarkup(resize_keyboard=True).add(KeyboardButton(f"CATs[{fds}]"))


    Код, конечно, жесть. По пунктам:
    1. НИКОГДА не генерируй SQL запрос через форматирование строк. Легко словить ошибку, а если очень не повезёт - SQL-инъекцию. Используй подстановку параметров (ссылка для sqlite, если у тебя другая БД - ищи доки на неё).
    2. Как насчёт проверить, а вернула ли БД хоть что-то? Это не гарантируется. Всегда ожидай подставы.
    3. Зачем итерироваться по индексу списка, если ты индекс не используешь? Почему не итерироваться по самому списку? И да, это абсолютные основы питона, которые НАДО знать задолго до того, как браться за ботов.
    4. Зачем ты создаёшь новую клавиатуру на каждой итерации цикла? Тут уже даже не знания языка, тут простая логика. Создал клавиатуру, добавил кнопку - на следующей итерации выбросил старую клавиатуру, создал новую добавил другую кнопку. Ну бессмыслица же! Создай клавиатуру один раз, до цикла, и добавляй кнопки к ней.
    5. Ну тот факт, что ты не посылаешь сообщение, к которому будет прикреплена клавиатура (а сама по себе она не бывает!), это уже мелочи. Видимо, не дошёл ещё до этого этапа.

    kbd = ReplyKeyboardMarkup(resize_keyboard=True)
    btns = cur.execute(f'SELECT name FROM cathlete WHERE id = ?', (message.from_user.id,)).fetchall()
    # btns - список списков. 
    if not btns:
        # кнопки не предусмотрены, как-то даём пользователю знать об этом
        return  # если это фатально, останавливаемся тут
        # иначе создаём какое-то значение btns по умолчанию и  продолжаем
        # тут уж сам решай
    # делаем клавиатуру
    for btn in btns:
        kbd.add(KeyboardButton(btn[0]))
    # ну и отправляем её вместе с сообщением
    Ответ написан
    7 комментариев