Задать вопрос
Ответы пользователя по тегу Python
  • Необходимо решить задачу для курса и понять в чем ошибка моего кода и мышления?

    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 комментария
  • Как сделать правильно?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    pogoda = pogoda.replace(' ', '-')
    Или в чем проблема?
    Ответ написан
  • Как сделать плейлист?

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

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Самый простой вариант - это забирать с ротуера логи и из них забирать инфу о подключении. Если это ваша сеть, то и настройки роутера вам доступны, а значит вы можете их анализировать.
    Если по какой-то причине нет возможности получать логи с роутера, то можно периодически сканировать локалку на предмет поиска новых компьютеров. Не важно на каком языке вы будете обрабатывать выхлоп утилит вроде nmap. Как этой и аналогичными утилитами просканировать сеть - это отдельный вопрос.
    В самом простом слуае вы можете написать на питоне утилиту, парсящую выхлоп nmap и перечень IP и mac-адресов кладущую в sqlite или тупо дописывающую в csv. Отдельной утилитой можно фильтровать получившийся журнал за указанные интервалы времени.
    Ответ написан
    Комментировать
  • Как сделать работу django и pyrogram одновременно?

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

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    . чтоВообще игнорировать любые исключения - это очень плохая идея. Причин этому много даже за рамками тех, с которыми вы уже столкнулись.
    Если вы немного почитаете про обраотку исключений, то увидите блоков except в этой конструкции может быть несколько и они фильтруют происходящие исключения в том порядке, в котором указаны.
    В общем случае следует различать два вида исключений: штатные и нештатные.
    Штатные - это те исключения о которых вы знаете, которые предусматриваете. В данном контексте это, например KeyboardInterrupt. Кроме того, вам нужно понимать какие ещё исключения порождает ваш код, какие из этих исключений нжно считать штатными, и поэтому как-то конерктно обрабатывать ил игнорировать, а какие считать нештатными.

    Нештатные иключения игнорировать и гасить - плохая идея. Каждое нештатное (непредусмотренное) исключения должо вести к поадению программы с подробным тейсбэком, который показывает где и почему произошла ошибка. Поняв из-за чего произошлоа нештатная ошибка, вы сможете либо устранить ошибку в коде, которая к ней приводит, либо обработаете её особым образом на нужном уровне так, чтобы эта ошибка стала штатной.

    Если ваш скрипт должен во что бы то ни стало работать и никогда не падать в продакшн-режиме, то можно поступить так:
    По факту KeyboardInterrupt делайте её рерайз. Остальные ошибки подробно логируйте и двигайтесь дальше:
    for item in items:
        try:
            ...
        except KeyboardInterrupt:
            raise
        except Exception as e:
            with open('errors.log', 'a') as log:
                log.write(f'{e}')
                # сюда же можно вывести трейсбэк, но я бы воспользовался стандартным логированием, чтобы не изобратать велосипед, однако его конфигурирование выходит за рамки данного отета.
    Ответ написан
    Комментировать
  • Как разбить массив на подмассивы используя "0" как разделитель?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Без сторонних либ я бы делал так:
    def split0(arr: list) -> list:
        res = [[]]
        for x in arr:
            res[-1].append(x) if x else res.append([])
        return res

    А вот для любителей функциональщины:
    from itertools import takewhile  
    def split0(a): 
        it = iter(a) 
        return [[x for x in takewhile(bool, it)] for _ in range(a.count(0) + 1)]
    Ответ написан
    2 комментария
  • Как перебрать все возможные варианты?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    [''.join(letters) for letters in itertools.product(string.ascii_uppercase, string.ascii_uppercase)]


    То же, но красивенько:
    [
        ''.join(xy)
        for xy in itertools.product(*(string.ascii_uppercase,) * 2)
    ]
    Ответ написан
    1 комментарий
  • Как преобразовать список в диапазон?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    years[1] += 1
    range(*years)

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

    В итоге я бы провалидировал список годов убедившись, что он имеет ровно два элемента и они в разумнух пределах.
    Ответ написан
    Комментировать
  • Что такое "типы данных" в Python?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Все типы данных - это классы, но не все классы - это типы данных. Хотя классы - это типы=) но не буду вас путать.
    "map, range, срез" - это не типы данных.
    Полагаю в том вашем вопросе од типами жанных имеют в виду простые типы вроде строк, чисел с плавающей точкой, целых чисел и даже комплЕксных.
    Кортежи (tuple) и словари (dict) - это уже структуры данных, коллекции... как угодно. Да, технически это тоже классы и в каком-то смысле их можно назвать типами, но все эти терминологические дебри вам ни к чему.
    Просто считайте типами данных атомарные значения простых типов. Если к вам докопаются при таком понимании, то надо таких слать=).
    Дальше интереснее. range, к примеру, это класс, инстанс которого "умеет" делать генератор арифметических последовательностей. Вот это вот слово "умеет" связано с питоновским понятием протоколов. К примеру, если к объекту можно обратиться по индексу, значит он поддерживает соответствующий протокол __getitem__. Если объект поддерживает срезы, то в этот метод можно подать специальный объект - инстанс slice. Этот инстанс просто несёт три параметра среза (начало, конец и шаг), а как его будет применять объект - это на откуп программисту.

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

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

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Можно подобрать одну из хороших мощных библиотек для удобной работы с дантами, чтобы там были методы перелистывания дат помесячно.
    Но если не хочется плодить зависимости, то я рекомендую штатно разобрать дату на день, месяц и год, убедиться, что день не превышает 28 (иначе такое число не в каждом месяце найдётся), а затем прибавить к месяцу 1 по модулю 12. В случае переполнения нужно увеличить еще и год.

    Такое элегантно записыается с помощью функции divmod:
    month -= 1
    year_increment, month = divmod(month + 1, 12)
    month += 1
    Ответ написан
    Комментировать
  • Что такое range() в Python 3?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    В целом всё верно. range реализоан как класс, при его инстанцировании (или вызове в качестве функции. что в питоне, суть, одно и тоже) формируется инстанс этого класса, который поддерживает протокол получения итератора.
    Если воспользоваться этим протоколом (iter(range(3, 33))), то получим итератор - инстанс класса range_iterator.
    Ещё инстанс range позволяет получать доступ к элементам по индексу, делать срезы, инвертировать, получать количество элементов, проверять на вхождение элемента и т.д.
    Ответ написан
    1 комментарий
  • Как удалить отступ добавляемый help для docstring?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Никак. Все отступы ровненько отрезаются форматировальщиком help и добавляется один отступ.
    Зачем вам этой дичью заниматься приспичило?
    Ответ написан
  • Как воспользоваться указателями в python?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Тут, похоже, имеется непонимание, как в питоне устроены переменные и чем они отличаются от переменных (и отчасти указателей) в Си.
    В Си (и не только, в паскале, например, тоже) переменная - это коробочка, которая уже есть и в неё можно что-то положить подменив какой-то мусор, который там был раньше, потом её содержимое можно заменить.
    В Питоне переменноая похожа на ярлычок. Ярлычок - это имя, а всё вокруг - это объекты. На один объект можно повесить много ярлычков (представьте себе ярлычок от чайного пакетика). Каждый ярлычок - это отдельное имя или позиция в списке, кортеже, словаре...) Обхект один, а ссылок на него может быть много. Ярлычок можно уничтожить или перевесить на другой объект. Когда не остаётся ни одного ярлычка, привязанного к объекту, он становится кондидатом на удаление и освобждение памяти.
    Вам в соседнем ответе показали как можно элегантно обменять именами два объекта в питоне. Да, именно, поменяются местами имена, но объекты как были так и останутся. Это как поменять таблички на кабинетах директора и главбуха.
    Так что отчасти все переменные в питоне - это умные слабые ссылки.
    Ответ написан