Ответы пользователя по тегу Python
  • Как получить последний элемент столбца?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    К соседнему ответу могу добавить.
    Если Id у вас формируется автоинкрементом, то можо ограничить выдачу select-запроса одним элементом и указать обратный порядок сортировки по нужному вам полю.
    Ответ написан
    Комментировать
  • Как округлить число на python до сотых?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Напомню на всякий случай, что не каждое десятичное число можно представить конечным количеством двоичных знаков. Это как 1\3 в троичной системе будет просто 0.1, а в десятичной 0.333(3).
    Это значит, что округлять с помощью round не всегда хорошая идея.
    К примеру, десятичное число 0.2 предствляется периодической бинарной дробью.
    Смотрите:
    In [1]: f'{0.2:.30f}'                                                          
    Out[1]: '0.200000000000000011102230246252'

    Если вам нужна точная арифметика с десятичными числами с правильным округлением, то используйте модуль decimal, где есть специальный тип данных, контроллирующий точность.
    В простых же случаях можно округлять до нужного числа знаков при преобразовании в текст, как показано выше. Однако при этом на способ округления (а там все не так просто, как некоторые думают) воздействовать нельзя. Если нужно совсем правильное округление - decimal.
    Ответ написан
    Комментировать
  • Как разбить строку на спискок?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Не стоит изобретать велоссипедов.
    В современном питоне есть замечательный модуль pathlib, который отлично работает с путями, файлами и директориями. Там есть всё, что нужно дл яправильной работы.

    Если нужно работать с неродными для вашей оерационной системы путями (например виндовыми из под линукса), то там есть специальный класс для этого:
    > p = pathlib.PureWindowsPath("E:\python\pycharm\fl\exel\ids.xlsx") 
    > p.parts
    ('E:\\', 'python', 'pycharm\x0cl', 'exel', 'ids.xlsx')

    Если вы работаете с путями нативно, то ещё прозе, модуль сам будет спользовать нужные классы:
    > p = pathlib.Path("E:\python\pycharm\fl\exel\ids.xlsx") 
    > p.parts
    ('E:\\', 'python', 'pycharm\x0cl', 'exel', 'ids.xlsx')

    Но под линукс такой код не поймёт чего вы от него хотите при передаче винлового пути. Для линукса это просто одно имя в текущем каталоге (слеши не те, двоеточие допустимо для употребления в имени).
    Ответ написан
    Комментировать
  • Как сформировать подсказки в Python при написании кода?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Чтобы IDE делало автоматические контекстные подсказки при написании кода, нужно писать код с докстрингами, указывать типы аргументов, не злоупотреблять "магией" с геттерами, сеттерами, метаклассами, декораторами, замыканиями и прочей эквилибристикой с кодом. Чем проще и каноничнее код, тем проще IDE его анализировать.

    А если вы интересуетесь как реалиовать эту функцию IDE амостоятельно, то по построению и содержанию вопроса могу заключить, что развернутый ответ вам не пригодится, ибо акими вопросами задаваться еще рано. Нужно подучиться.
    Ответ написан
    Комментировать
  • Как красиво использовать strptime с различными форматами?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Если тянуть зависимости не хотите и во что бы то ни стало желаете реализовать самостоятельно, а набор форматов, которые нужно поддерживать, понятен, то можно селать так.
    Делаете список форматов в порядке оценки невозрастания вероятности его применения.
    По списку форматов идёте циклом и пробуете каждый. Попытку парсинга внутри цикла заворачиваете в try\except и при отсутствии ошибки произойдёт спешный return, а по факту завершения цикла райсите свою ошибку о том, что не удалось подобрать формат для парсинга даты.
    Ответ написан
    2 комментария
  • Как правильно подать GET запрос чтобы страница изменилась?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Смотрите. Вот у вас задача - получить отзывы так, чтобы новые были сверху.
    Вы дёргаете урл страницы с отзыывами и суёте в заголовки параметр с указанием сортировки. Но с чего вы вообще взяли, что АПИ принимает этот параметр через заголовки?

    Как следовало бы вам решать эту проблему. Метод годный для многих случаев.
    Нажимайте в хроме F12, выбирайте вкладку Networking и меняйте сортировку (мышкой на страничке). Смотрите, там появляется новый запрос, который страница делает через аякс не перезагружаясь полностью:
    https://yandex.ru/maps/api/business/fetchReviews?a...
    У вас запрос по конкретно этому урлу не выдаст данных, поскольку scrf-токен будет не актуальным и сессия не будет валидной. Чтобы получить токен и сессию вы можете сделать штатный гет запрос к странице. как вы делали в своём посте.
    Имея необходимые токены вы можете делать запросы с параметром ajax и не придётся парсить страницу выуживая данные, они будут сразу в json.
    Ответ написан
    2 комментария
  • Telegram автоматическое открытие ссылок Privnote?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Готового решения я не знаю, но вполне можно написать такую тулзу с использованием клиентского апи телеграмма. Она будет от вашего имени коннектиться в телегу и ждать сообщения в чате, как только оно придёт, открывать ссылку и... ну там как запрограммируете.
    Ответ написан
    Комментировать
  • Можете оценить код python?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Общепринятый ключ в такого рода утилитах для рекурсивной обработки подкаталогов -r/--recursive. Ваш вариант интуитивно не очень понятный.
    Сразу хочется придраться к наименованию основной функции и переменных. view_folder - подразумевает по смыслу некое неизменяющее воздействие, просмотр или что-то эдакое, но не то, что по факту делает её код. process_folder или clean_unpaired_raws подощло бы больше. А если добавить докстринг и тайпхинтинг для параметров, было бы вообще замечательно.
    И да, параметры следует тоже называть консистентно. У вас одно и то же называется в программе сильно по-разному. Зачем вносить неразбериху? Зачем экономить несколько символов в ущерб понятности и читабельности?
    Параметры следовало бы передавать по имени. Из-за неочевидного и неконсистентного именования приходится каждый раз смотреть что туда передаётся, причем внимательно следить, отсчитывая параметры позиционно и читать что про соответствующий параметр пишется в описании аргпарсера. Код должен быть максимально прозрачным даже для дго, кто первый раз его видит.

    Если уж вы запросили ревью, то буду цепляться по порядку без учета степени и значительности недочетов. Какие-то вещи вполне норм, но я бы сделал иначе, их тоже буду упоминать.
    Например, я бы в main передавал вектор аргументов с умолчанием в виде None. При передаче None доставал бы вектор из sys.argv[1:], как это делает парсер аргументов внутри, а при передаче конкретного вектора, использовал бы его. Это помогло бы протестировать парсинг параметров, если до таких автотестов дошли бы руки.

    То, что у вас в основной функции, принято называть спагетти-кодом. Следует разделять код на удобные для понимания и тестирования части.

    Я бы вынес в отдельную функцию формирование итератора по файлам.
    Отдельной безопасной функцией генерил бы дифф.
    Отдельно бы этот дифф применял. Отдельно (и опционально) спрашивал бы подтверждения пользователя.
    Часто от утилиты требуется "тихая" работа или работа без лишних вопросов к пользователю. Для этого даже применяются специальные ключи, вроде -y/--yes.

    Почему бы не импортнуть from pathlib import path вместо постоянного обращения pathlib.Path? Где-то экономите букву, а где-то вот так вот...

    Вот это условие:
    pathlib.Path(str(file.parent/file.stem) + img).is_file()

    Следовало бы записать так:
    file.with_suffix(img).exists()
    Это короче и правильнее. Есть нюанс. У вас с таким именем может быть не только файл, но и каталог. Об этом часто забывают. Я бы в таких неоднозначных ситуациях следовал более безопасному поведению, например сохранению рав-файла, если соответствующая картинка - не картинка, а вообще каталог.
    if 'raw_file' in str(file): #Если файл ранне был перемещен в папку raw_file его не игнорируем
        continue

    Игнорируете файлы в каталоге, заданном "магической константой". А если у пользователя такие каталоги используются для других целей?
    В идеале нужно хотя бы задекларировать такое поведение на уровне документации.
    Я бы ввёл перечень подкаталогов, которые игнорируются при сканировании. Позволил бы указывать название каталога, куда скидывать равы, в параметрах утилиты, добавлял бы это имя в список игнорируемых пол умолчанию каталогов.
    Позволил бы список игнорируемых каталогов изменять.

    Вы проверяете принадлежность фала подкаталогу проверкой вхождениея подстроки в его полное имя, но это неправильно, ведь такой кусок строки может быть в составе другого каталога или файла.
    Например в полном пути каталога, который нужно почистить будетет что-то вроде такого:
    "/home/dave/please_clean_raw_files_here/vacations2020/my_camera/IMG1010.RAW"
    Все равы в "/home/dave/please_clean_raw_files_here/vacations2020/my_camera/" будут проигнорированы.
    Неожиданно, правда?

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

    Часто у рав-файла и JPG одна и та же дата-время в свойствах файла или в exif. Это позволит навярняка удалять только гарантированно непарные файлы, и вы не зацепите случаи, когда у камеры сбросился или провернулся счетчик кадров. Также решится проблема с ситуацией, когда в одном каталоге есть фотосеты с разных камер, которыые именуют фотки аналогичным образом. Очень трудно совпасть по времени кадра до долей секунд.

    Вот в таких сучаях следует не следует инвертировать условие и углублять код, превращая его в спагетти:
    if not len(difference) == 0:
        folder = str()
        print('\nRaw without jpg:')
        for file in difference:
            if not folder == file.parent:
                folder = file.parent
                print(f'\nFolder: {folder}')
            print(f'- {file.name}')
    else:
        sys.exit(f'\nThere are no suitable files in the {path} folder\n')

    Смотрите, если по одной из веток происходит выход, то его нужно сделать сразу, чтобы не забивать голову лишними отступами и уровнями ветвления:
    if not difference:  # да в питоне так проверяют пустоту списка
            sys.exit(f'\nThere are no suitable files in the {path} folder\n')

    А ещё обратите внимание, что '\nRaw without jpg:' выведется даже если ключи заставляют утилиту удалить все равы, даже те, к которым есть парные картинки. То есть ваша тулза откровенно врёт пользователю в некоторых ситуациях.

    Вот здесь вы фактически группируете файлы по каталогам:
    for file in difference:
        if not folder == file.parent:
            folder = file.parent
             print(f'\nFolder: {folder}')
        print(f'- {file.name}')

    Отчего бы не вынести это в отдельную функцию, чтобы не захламлять код?
    Да, она использовалась бы лишь единожды, но будучи понятно названной и понятно задокументированной эта функция сделает ваш код гораздо прозрачнее. Её вызов будет нести понятные последствия, а из спагетти-кода главной функции уйдёт приличный кусок, он будет заменен одним вызовом.
    Обратите внимание на tertools.groupby. Она умеет группировать последовательность по результату лямбды и вы сможете сделать генератор генерторов, которые будут выдавать вам списки файлов по каталогам.

    Пчему бы не заменить все вот такие куски на отдельную функцию с булевским результатом?
    while True:
        confirm = input('\nMove this files to a folder "raw_files"? press: y/n: ')
    [...]

    Пусть мучает пользователя бесконечно, пусть сделает системный выход, если приспичит, а лучше вальнётся с исключением, чтобы можно было при ее применении освободить ресурсы по finally.
    Зато её вызов был бы простым и лаконичным:
    if confirm('\nMove this files to a folder "raw_files"? press: y/n: '):
        move_file(difference, difference_folder)
        sys.exit('Complete\n')


    Почему у вас функция move_file называется так, будто перемещает один файл, аргумент у нее наывается в единственном числе, а принимает она туа целый список путей?!!
    Это либо злонамеренность, либо халатность. Нельзя так относиться к коду. Вы его пишете и на ходу меняете не думая о названиях, не думая о документации, не думая о консистентности. Полагаю раньше эта функция отвечала за перемещение только одого файла, а потмо вы вспороли ей брюхо и заставили обрабатывать список, но не позаботились ни о правильном названии, ни об изоляции... Франкенштейн у вас получился.

    Дважды вычисляете одно и то же значение:
    if not pathlib.Path(file.parent / 'raw_files' / f'{p.stem}({i}){file.suffix}').is_file():
        pathlib.Path(file).replace(file.parent / 'raw_files' / f'{file.stem}({i}){file.suffix}')

    У вас тут и магическая константа в коде на каждом шагу, и неправильно импортированный pathlib... Cнова забыли, что помимо файлов бывают каталоги и каталог с определённым именем просто сломают логику вашей тулзы.
    Отсутствует корректная обработка ошибок. Открытый в каком-нибудь редакторе файл или папка с расширением .raw поломает к чертям всю работу и оставит её в некоторых случаях недоделанной, а это ещё хуже, чем вовсе не сделать ничего.

    Зачем вы каждый раз делаете приведение к Path?
    pathlib.Path(f / 'raw_files')
    Результат конкатенации с Path будет тоже Path. Нет смысла приводить тип? И так в куче мест по всему коду!

    Почему бы не сделать одну функцию по побработке одного единственного файла?
    def move_raw_file(file: Path, dest_folder: typing.Union[Path, str None]=None, exists='safe'):

    Переносит файл `file` в каталог dest_folder или удаляет, если он задан как None.
    dest_folder может быть задан абсолютно или относительно родительского каталога file.
    `exists`: safe, replace, ignore, error
    В общем всё плохо. Учиться и учиться. Но бывает и хуже. так что дерзайте.

    Для подобной утилиты ка кдля врача важно не навредить. Плохо будет, если вашу утилиту кто-то завернёт в GUI или засунет в пакетный файл научив неискушенного пользователя приводить в опрядок свою коллекцию фоточек одном кликом. В один прекрасный день с таким "качеством" написанная утилита перемешает фотки пользователя или, что хуже, удалит их часть.

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

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Не то чтобы это был валидный ответ на ваш вопрос, мне кажется вы могли бы легко найти с десяток готовых планировщиков на базе syncio или даже готовое приложение на джанго с админкой, где можно этими напомналками управлять.
    Я делал планировщик игровых событий на торнадо в рамках игрового движка. Игровые события планировались на интервалы от миллисекунд, до нескольких суток (ММОРПГ). В эентлупе они сортируются, а при срабатывании события мета-данные этого события порождают новые и пихают их в эвентлуп.
    В чем конкретно ваши сложности? Задача не выглядит неподъёмной. Увы, готовое решение предложить не смогу, но уверен такие есть на гитхабе.
    Ответ написан
    Комментировать
  • Как сбросить цикл?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    random.seed()
    перед получением рандомных значений.
    Ответ написан
    Комментировать
  • Как внутри хендлэра вызвать функцию с другого хендлэра (aiogram)?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Вынесите всё тело хендлера в отдельную асинхронную функцию и вызывайте ее и в хендлере и там, где еще требуется. Само собой надо через await ее вызывать
    Ответ написан
    3 комментария
  • В чем заключаются архитектурные ошибки моего кода?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    class Calculation():
    
      def __init__(self, calculation):
        #init
        self.calculation = calculation

    Вы сделали класс `Вычисления`, чтобы проводить вычисления, пока проводятся вычисления вычислений над вычислениями, которые вычисляются как аргумент вычислений для вычисления состояния вычислений.

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

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

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

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

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

    Вот тут у вас, очевидно, можно написать проще и без лишних повторений, провоцирующих ошибки:
    if i % self.inflation_indexation_period==0 and i != 0:
            if i not in range(0, self.inflation_indexation_period):
              init_indexation_inflation *= self.sales_init.inflation_indexation
            else:
              init_indexation_inflation = 1
            inflation_indexations.append(round(init_indexation_inflation, 5))
          else:
            inflation_indexations.append(round(init_indexation_inflation, 5))

    Лучше так:
    if i % self.inflation_indexation_period==0 and i != 0:
            if i in range(self.inflation_indexation_period):
              init_indexation_inflation = 1
            else:
              init_indexation_inflation *= self.sales_init.inflation_indexation
    
          inflation_indexations.append(round(init_indexation_inflation, 5))

    Функция get_inflation_indexations у вас имеет опасный побочный эффект. Она имеет префикс get_ но модифицирует контекст объекта. Это кэширование? Чем обусловлено такое поведение? Если такое делается "на всякий случай". то это плохая практика неявного внедрения побочного эффекта. Если нарочно, то такое надо документировать и корректно называть и описывать метод в докстринге.

    Опять же, get_inflation_indexations и get_value_indexations очень похожи по коду. Это повод вынести такую логику в отельную функцию, она будет проще и её будет проще тестировать!
    А у вас эти функции отличаются именами атрибутов внутри и магическими константами, которые в коде делать не хорошо, тем более без пояснений, тем более в кусках такого похожего кода.

    Перестаньте использовать i в качестве переменной для итерирования нетривиальных сущностей, отличных от протсого счетчика. i - это индекс. Используйте человеко-понятное название переменной для этого!

    Используйте декоратор итераторов enumerate. Это сделает код более прозрачным и читабельным, чем код с параллельными счетчиками. Увидев enumerate читатель кода сразу поймёт, что это простой счетчик итерируемых сущностей, что не нужно ожидать скачков этого счетчика и каких-то сложных корреляций.

    А вот здесь вообще всё плохо:
    count = 0
        revenue_list = []
        for i in total_price:
          revenue = i*total_value[count]
          revenue_list.append(revenue)
          count+=1

    count - это "количество", а вы его используете как "индекс" и никак иначе!
    i - это индекс, а вы туда суёте фактически цену!
    У вас total_price и total_value параллельные одноразмерные списки, их нужно состегнуть с помощью zip и пронумеровать с помощью enumerate (если надо, а здесь не надо!).
    Весь этот кусок понятнее, проще, короче и более питоничнее записать в такой форме:
    revenue_list = [price * value for price, value in zip(total_prices, total_values)]


    Итого вся вот эта громоздкая плохо читабельная функция:
    def get_revenue(self):
        '''Получить итоговую выручку'''
        total_price = []
        for i in self.get_inflation_indexations():
          price = self.sales_init.price*i
          total_price.append(price)
    
        total_value = []
        for i in self.get_value_indexations():
          value = self.sales_init.sales_volume*i
          total_value.append(value)
    
        count = 0
        revenue_list = []
        for i in total_price:
          revenue = i*total_value[count]
          revenue_list.append(revenue)
          count+=1
    
        return revenue_list

    Легко и читабельно для питониста заменяется на вот такую:
    def get_revenue(self):
        '''Получить итоговую выручку'''
        indexations = self.get_inflation_indexations()
    
        init_price = self.sales_init.price
        total_prices = [init_price * x for x in indexations]
    
        init_volume = self.sales_init.volume
        total_values = [init_volume * x for x in indexations]
    
        return [price * value for price, value in zip(total_prices, total_values)]


    И везде не стоит использовать параллельные счетчики, используйте итераторы, распаковку, зипы, енумервторы и функциональный стиль, ведь он сокращает код и делает его проще.

    Что это за ерунда:
    def get_interest_expenses(self):
        '''процентные расходы'''
        interest_expenses_list = []
        return interest_expenses_list


    Зачем много раз считать одно и то же целиком, чтобы взять только очередной кусочек из всего посчитанного?!
    Это вообще бред. Учитесь основам алгоритмизации и не надо программировать на питоне как не на питоне.

    Вот такое вообще жесть: self.get_revenue()[count]
    Отчего не сохранить в промежуточную переменную?!

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

    Удачи.
    Ответ написан
    3 комментария
  • Что не так, библиотека telebot?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Потому, что переменной message у вас в коде нет, а обращение к ней есть. В сообщении об ошик едаже процитирована строчка, где это происходит, прочитайте внимательно.
    Ответ написан
    2 комментария
  • Не создается срез в python. Ошибка TypeError?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Давайте я помогу перевести этот текст для вас:
    TypeError: slice indices must be integers or None or have an __index__ method

    - Ошибка Типов: индексы среза должны быть целочисленными значениями или None или должны поддерживать метод __index__.
    Теперь посмотрите внимательно на строку, где произошла ошибка:
    full_downtime = string[downtime_border1:downtime_border2]

    Где же у вас тут индексы среза?
    -- Вот они: downtime_border1 и downtime_border2
    Давайте посмотрим какие значения у вас могут принимать эти переменные:
    downtime_border1 = ''
    downtime_border2 = ''

    Неожиданно!
    Что это а тип? Это целые числа? - нет
    Это None? - Нет!
    У них есть метод __index__?
    А что, черт возьми, вы хотели этим сказать в своей программе?
    На лицо не понимание кода. Вам слеует немного поучить основы питона.
    Ответ написан
    5 комментариев
  • Необходимо решить задачу для курса и понять в чем ошибка моего кода и мышления?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Александр Нестеров привёл решение на псевдокоде, но вопрос под тегом Питон... Дело в том, что питон не поощряет изменение входных строк в решениях такого рода.
    Поэтому приведу своё решение перфекциониста=) Если кто укажет как его улучшить, буду рад=)
    Возможно новичку применённые там трюки будут сложны в понимании или даже местами вредны, но точно познавательны.

    import unittest
    import typing
    
    _VARIANTS = []  # Сюда попадут все решения, которые нужно тестировать
    # А это декоратор, которым нужно пометить все претенденты на решение
    solution = lambda f: _VARIANTS.append(f) or f
    
    EXAMPLES = [
        ('', ''),
        ('a', 'a1'),
        ('abc', 'a1b1c1'),
        ('aaaabbca', 'a4b2c1a1'),
    ]
    
    
    @solution
    def encode(inp: str) -> str:
        """Самое простое решение
        Минусы: громоздко и многословно 
        (зато прозрачно и понятно, без магии и выкрутасов)
        """
        if not inp:
            return ''
        count = 1
        current = inp[0]
        res = []
        for c in inp[1:]:
            if c == current:
                count += 1
            else:
                res.append(f'{current}{count}')
                count = 1
                current = c
        
        res.append(f'{current}{count}')
        return ''.join(res)       
    
    
    def encode_stream(inp: typing.TextIO) -> typing.Iterator:
        """Потоковое решение. 
        С помощью него можно без затрат памяти кодировать файлы любого размера
        Минусы: нет, если вам нужно закодировать гигабайты. 
        Ну или готов поспорить на эту тему, если вы не согласны.=)
        """
        current = inp.read(1)
        count = len(current)
        while current:
            c = inp.read(1)
            if c == current:
                count += 1
            else:
                yield f'{current}{count}'
                current = c
                count = len(current)
    
    
    @solution
    def encode_string_by_stream(inp: str) -> str:
        """Обёртка для использования потокового кодирования из строки"""
        import io
        return ''.join(encode_stream(io.StringIO(inp)))
    
    
    @solution
    def encode_elegant(s: str) -> str:
        """Довольно элегантное решение на словаре от @seven5674.
        К сожалению в оригинальном варианте неверное, но я 
        исправил и отрефакторил.
        Минусы: запутанное и непрозрчное, зато короткое"""
        d = {}
        g = 1
        for c in s:
            g = d.get((c, g), 0) and g or g + 1
            d[c, g] = d.get((c, g), 0) + 1
    
        return ''.join([f'{k[0]}{v}' for k, v in d.items()])
    
    
    @solution
    def encode_by_regexp(s: str) -> str:
        """Решение на регекспах от @seven5674, 
        я лишь чуть отформатировал и отрефакторил.
        Минусы: регексп поведёт себя довольно непредсказуемо на больших данных,
        к тому же регекспы читать умеют не все. 
        Но автор умеет в регекспы лучше, чем в питон, видимо с js пришел.
        """
        import re
        return ''.join(
            f'{w}{len(l)}' 
            for l, w in 
            re.findall(r"((\w)+?(?!\2))", s)
        )
        
    
    #############################################################################
    ## Дальше идёт инфраструктура для тестирования решений
        
    class Test(unittest.TestCase):
        """Автоматический тест решений.
        Претенденты на решение должны быть помечены декоратором @solution
        Примеры берутся из списка EXAMPLES.
        """
    
        def closure(func, arg, res):
            """Временная функция, которая делает тест.
            Она формирует каждый раз новую функуию-замыкание, которая будет тестировать
            оережной кейс.
            """
            def test(self):
                f"""Тест функции {func.__name__}({arg!r})"""
                self.assertEqual(func(arg), res, msg=f'Func {func.__name__}({arg!r})')
            return test
        
        # Перебираем все варианты реализаций:
        for f in _VARIANTS:
            # Перебираем все предлженные эталонные римеры:
            for case_num, case in enumerate(EXAMPLES):
                # Создаём новую функуию теста и добавляем ее в класс теста как метод.
                locals()[f'test_{f.__name__}__{case_num}'] = closure(f, *case)
    
        # Удаляем из контекста класса лишние переменные
        del(closure, f, case_num, case)
    
    
    if __name__ == '__main__':
        unittest.main(verbosity=3)


    UPD: Обновил код, включив туда решения seven5674 и valerr007
    Кстати, решение valerr007 проваливает тест с "a" на входе. Возвращает пустую строку. Ну и много претензий к коду.
    Ответ написан
  • Как вступать в чаты telegram по всем видам ссылок?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Бот сам не может вступать в чаты, его должны добавлять люди.
    Иначе телеграмм бы давно утонул в спаме
    Ответ написан
  • Почему при вводе input() выдается ошибка?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Потому, что вместо числа вводишь пустую строку.
    Вот же написано, что неправильный литерал для десятичного числа. И даже указано какой именно литерал - пустая строка.
    А откуда она там? Видимо от того, что винда подаёт в буффер сдва символа /r/n в качестве абзаца.
    Оба они отсекают ввод через input, между ним ипустота. Так что до ввода 1 дело не доходит.
    Ответ написан
  • Как реализовать колоду карт через классы на python?

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

    Сделайте карту универсальной, сделайте ее на основе typing.NnamedTuple.
    Пусть у каждой карты будет весь набор атрбутов, но только один какой-то будет ненулевым. Не нужно тут наследования, ведь у вас не используется полиморфизм.

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

    Вот пример:
    import datetime
    import itertools
    from typing import NamedTuple
    
    
    class Card(NamedTuple):
        name: str = None
        phys_damage: int = 0
        hp: int = 0
        mage_damage: int = 0
        armor: int = 0
        add_hp: int = 0
    
        def __repr__(self):
            """repr у нас будет показывать только аргументы, которые отличаются от умолчательных"""
            defaults = type(self)._field_defaults
            params = ', '.join(
                f'{k}={v!r}' 
                for k, v in self._asdict().items() 
                if k in defaults and v != defaults[k]
            )
            return f'{type(self).__name__}({params})'
    
        def __str__(self):
            return f'<{self.name}#{id(self)}>'
            
        @property
        def total_hp(self):
            return self.hp + self.add_hp
    
        @property
        def group(self):
            return max(
                (self.mage_damage, 'Intelegence'),
                (self.add_hp, 'Strength'),
                (self.armor, 'Agility'),
            )[1]
        
    
    class Deck():
        def __init__(self, deck_name="Колода", date=None, cards=None):
            self.deck_name = deck_name
            self.date = date or datetime.now()
            self.cards = cards or []
    
        def __iter__(self):
            return iter(self.cards)
        
        def by_group(self):
            return itertools.groupby(self, lambda card: card.group)
    
    my_deck = Deck("Моя колода", "24.05.2022", [
        Card("Течис", phys_damage=32, hp=600, mage_damage=100),
        Card("Земеля", phys_damage=40, hp=600, mage_damage=60),
        Card("Рудге", phys_damage=80, hp=600, add_hp=150),
        Card("Крип", phys_damage=60, hp=600, armor=10),
    ])
    
    
    for group, cards in my_deck.by_group():
        print(f'# {group}:')
        for card in cards:
            print(f'\t{card!r}')
    Ответ написан
  • Почему не работает?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Потому что иногда поиск что-то находт и дает результат, а ногда не находит и возвращает None
    Ответ написан
    3 комментария
  • Порядковый генератор фразы?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Это делается так:
    itertools.product(*[words] * len(words))
    Но вам, похоже, рано решать такие задачи, ведь вы совсем не знаете язык. Да и задания на этом ресурсе против правил. Лучше изучайте питон, а не жульничайте тут с решением задач.

    UPD:
    А можно и вот так вот рекурсивно:
    def r(w=words, n=5): 
        if n > 1: 
            yield from ([i] + rr for i in w for rr in r(w=w, n=n-1)) 
        else: 
            yield from ([i] for i in w)

    Максимальная глубина рекурсии - n
    Ответ написан
    3 комментария