TGTGInsighttelegram intelligenceLIVE / telegram public index
Назад кон каналите
Python Заметки avatar

TGINSIGHT CHAT

Python Заметки

@pythonotes

Education

Интересные заметки и обучающие материалы по Python Контакт: @paulwinex ⚠️ Рекламу на канале не делаю!⚠️ Хештеги для поиска: #tricks #libs #pep #basic #regex #qt #django #2to3 #source #offtop

Претплатници2,220Тековни претплатници
Следени објави384Број на индексирани објави
Неодамнешен опфат5,331Збир на неодамнешни прегледи
Неодамнешни објави

Неодамнешни објави

Страница 30 од 32 · 384 објави

Објавено 16 фев.

Стандартная функция enumerate() очень удобна для получения индекса итерации >>> mylist = ['one', 'two', 'three'] >>> for i, item in enumerate(mylist): >>> print(i, item) 0 one 1 two 2 three Вот бы со словарями так! Чтобы удобно получить индекс, ключ и значение! Это можно сделать так: >>> mydict = {10: 'item1', 20: 'item2', 30: 'item3'} >>> for i, key in enumerate(mydict): >>> value = mydict[key] >>> print(i, key, value) 0 10 item1 1 20 item2 2 30 item3 Третья строка явно лишняя, приходится дополнительно доставать значение по ключу. Как нам сократить код? Хочется распаковать ключ и значение сразу. Например так: >>> for i, key, value in enumerate(mydict.items()): >>> print(i, key, value) И получаем ошибку ValueError: not enough values to unpack (expected 3, got 2) Справедливо, ведь enumerate() возвращает всегда кортеж из 2х элементов, а мы распаковывем его в 3 переменные. Но есть одна хитрость, которая позволит сделать то что мы задумали! Скобочки! >>> for i, (key, value) in enumerate(mydict.items()): >>> print(i, key, value) 0 10 item1 1 20 item2 2 30 item3 Теперь норм 😎 #tricks

467 views

Hashtags

Објавено 14 фев.

Чтобы запустить два контекст менеджера одновременно можно написать так: with open('script.py') as inp: with open('_bkp.py', 'w') as out: out.write(inp.read()) А можно и более компактно в одну команду: with open('script.py') as inp, open('_bkp.py', 'w') as out: out.write(inp.read()) #tricks

431 views

Hashtags

Објавено 12 фев.

Как соединить два списка? Список поддерживает оператор "+", так что это легко: >>> l1 = [1, 2, 3] >>> l2 = [4, 5, 6] >>> l3 = l1 + l2 >>> print(l3) [1, 2, 3, 4, 5, 6] А как тоже самое повторить со словарями? >>> d1 = dict(k1=1, k2=2) >>> d2 = dict(k3=3, k4=4) >>> d3 = d1+d2 TypeError: unsupported operand type(s) for +: 'dict' and 'dict' Да, словари такой оператор не поддерживают. Самое распространённое решение это метод словаря update() d1.update(d2) Но это способ изменяет один из словарей, а нам нужно оставить оба словаря нетронутыми и создать третий. Это можно сделать таким способом: d3 = dict( d1.items() | d2.items() ) Или еще короче d3 = {**d1, **d2} Если количество словарей неизвестно и все они в одном списке, то вариантов еще больше. Допустим, есть такой список со словорями: dict_array = [ {'k1': 1, 'k2': 2}, {'k3': 3, 'k4': 4}, {'k5': 5, 'k6': 6}, ] Для объединения всех в один мегасловарь делаем так: >>> from operator import or_ >>> from functools import reduce >>> d3 = dict(reduce(or_, [x.items() for x in dict_array])) {'k3': 3, 'k6': 6, 'k1': 1, 'k4': 4, 'k5': 5, 'k2': 2} Если важна последовательность ключей, то еще есть способ >>> from itertools import chain >>> d3 = dict(chain.from_iterable(d.items() for d in dict_array)) {'k1': 1, 'k2': 2, 'k3': 3, 'k4': 4, 'k5': 5, 'k6': 6} Или так d3 = dict(chain(*map(dict.items, array))) Есть вариант и покороче >>> from collections import ChainMap >>> d3 = dict(ChainMap(*array)) >>> print(d3) {'k1': 1, 'k5': 5, 'k6': 6, 'k3': 3, 'k2': 2, 'k4': 4} И даже еще короче, в одну строку и с сохранением порядка: >>> d3 = dict(j for i in array for j in i.items()) >>> print(d3) {'k': 1, 'k2': 2, 'k3': 3, 'k4': 4, 'k5': 5, 'k6': 6} Можно ещё несколько вариантов придумать, но, думаю, достаточно 😁 __________ Все примеры создают новый словарь, не изменяя старые. Но если в исходных словарях есть другие словари или списки то для независимой копии нужно пройтись еще функцией copy.deepcopy()😉 #tricks

503 views

Hashtags

Објавено 10 фев.

Простая задача: получить рандомную строку. Такое может потребоваться для создания секретного ключа, токена, одноразовой ссылки и тд. Как бы вы решали такую задачу? Допустим, требуется строка в 20 символов. Чаще всего решают примерно так: from string import ascii_letters, digits from random import choice token = ''.join(random.choice(string.ascii_letters+string.digits) for _ in range(20)) Редко, но встречается и такой способ import uuid token = str(uuid.uuid4())[:20] Но самый верный способ это готовый модуль secrets (Python3.6+) из стандартной библиотеки. import secrets token = secrets.token_urlsafe(15) или token = secrets.token_hex(10) Почему лучше? - Короче запись - Более читаемый и понятный код - Быстрей работает ⏱ Время на 1М запусков: - random+string : ~14.3sec - uuid : ~5.5sec - модуль secrets : ~1.2sec ________ Прошу не путать получение рандомной строки и получение контрольной суммы. Это разные по назначению задачи. #tricks

422 views

Hashtags

Објавено 9 фев.

😉 Трик про flatten-список. Задача: из списка списков сделать одноуровневый список >>> arr = [[1, 2], [3, 4], [5, 6]] Допустим, есть список интов arr = [1, 2, 3, 4, 5] Как получить их сумму? Очень просто! >>> sum(arr) 15 И тут вы подумаете: Вау, какой удобный метод. Он просто берет список объектов и склеивает их через "+". Удобно же! Такс, если list, как тип, поддерживает оператор сложения, то я же могу тогда сделать такой финт: >>> arr = [[1, 2], [3, 4], [5, 6]] >>> sum(arr) Traceback (most recent call last): File "<input>", line 2, in <module> TypeError: unsupported operand type(s) for +: 'int' and 'list' О нет! Счастье было так близко! 😭😢😩 Стоп, давайте разбираться. Если функция sum() просто прибавляет очередной аргумент списка к предыдущему, то с чем складывается самый первый элемент? Должно быть какое-то стартовое значение. И оно есть, это ноль "0". Потому-то мы и видим такую ошибку. На наше счастье мы можем указать стартовое значение вместо ноля, чтобы получить сумму, используя в качестве начала другое число: >>> arr = [1, 2, 3, 4, 5] >>> sum(arr, 5) 20 И, следуя этой логике..... >>> arr = [[1, 2], [3, 4], [5, 6]] >>> sum(arr, []) [1, 2, 3, 4, 5, 6] YESSSS!!!! 😎🥰🤟 Вы, скорее всего, ломанётесь проверять другие типы и будете правы. С другими тоже работает. Но Python не упустит случай вас потроллить))) >>> words = ['Hello ', 'world', '!'] >>> sum(words, '') TypeError: sum() can't sum strings [use ''.join(seq) instead] Не используйте "+" для склейки строк! Скорее всего именно эта ошибка сделана на случай если вы захотите склеить большой текстовый документ, прочитанный через readlines(). _______ Это далеко не единственный способ сделать flatten-список. Пост скорей про функцию sum. #tricks

424 views

Hashtags

Објавено 7 фев.

⭐️ Между прочим... Читаете мой канал на телефоне? Код в примерах бывает растягивается в достаточно длинные строки. На мобиле смотреть не очень удобно. Особенно, учитывая, что переносы в Python это тоже синтаксис. Но если повернуть телефон горизонтально, то всё становится куда приятней! Казалось бы, это же очевидно! Но я сам не сразу догадался😁

397 views

Објавено 7 фев.

Скорее всего уже слышали, что складывать строки через + это плохая практика. Падение производительности, и всё такое. Без лишних слов, давайте измерять: from timeit import timeit def t1(): # складываем 10 строк через + из переменной t = 'text' for _ in range(1000): s = t + t + t + t + t + t + t + t + t def t2(): # склеиваем список строк через метод join arr = ['text'] * 10 for _ in range(1000): s = ''.join(arr) def t3(): # складываем через + но не из переменной а непосредственно инлайн объекты for _ in range(1000): s = 'text' + 'text' + 'text' + ... # всего 10 раз Теперь каждую строку склейки запустим по 10М раз >>> timeit(t1, number=10000) 0.21951690399964718 >>> timeit(t2, number=10000) 1.4978306379998685 >>> timeit(t3, number=10000) 0.2213820789993406 Хм, а нам говорили что через "+" это плохо и медленно ))) 😁 Тут стоит учитывать, что речь идёт о склейке множества длинных строк. Давайте изменим условия: def t4(): t = 'text'*100 for _ in range(1000): s = t + t + t + t + t + t + t + t + t def t5(): arr = ['text'*100] * 10 for _ in range(1000): s = ''.join(arr) def t6(): for _ in range(1000): s = 'text'*100 + 'text'*100 + ... # всего 10 раз >>> timeit(t4, number=10000) 12.795130728000004 >>> timeit(t5, number=10000) 2.642637542999182 >>> timeit(t6, number=10000) 0.2184546610005782 Вот, уже другой разговор, сразу видна разница, в среднем в 6 раз. Но погодите, почему последний тест t6() по скорости такой же как и t3()? Ведь строки теперь в 100 раз длиннее! Это вопросы оптимизации кода, какие простые изменения ускоряют или замедляют выполнение программы. Мы столкнулись с примером обхода обращения к переменной. Например, именно так работает директива #define в С++, во время компиляции подставляя значение переменной вместо ссылки на неё. В Python это тоже работает, но часто ли вы сможете встретить такой способ работы со строками? К сожалению, способ почти только теоретический. В целом, тесты показали то, что мы хотели. Делаем выводы самостоятельно. Полный листинг 🌍 #tricks

416 views

Објавено 5 фев.

Сколько вы знаете базовых "легальных" способов форматирования строк с помощью переменных в Python? Я насчитал 5! 1️⃣Оператор + >>> greting = 'Hello' >>> name = 'World' >>> print(greting + ' ' + name) 'Hello World' Самый не актуальный способ форматирования. С мелкими строками в количестве двух штук он работает быстрее всех. Но когда строки становятся длинной в сотни символов, этот метод просаживает производительность. Делов том, что каждый оператор "+" выполняется отдельно для пар переменных, создавая новый объект строки, после которого следует следующий оператор, создающий еще один объект итд... В общем не рекомендуется, кроме случаев, где он явно выигрывает по скорости. 2️⃣Оператор % >>> name = 'World' >>> print('Hello %s' % name) 'Hello World' Быстрый, но не удобный. Устарел. 3️⃣Метод str.format() Есть много фишек применения этого метода. Для примера используем самый простой. >>> name = 'World' >>> print('Hello {}'.format(name)) 'Hello World' Наверное, самый функциональный метод с множеством возможностей. Рекомендован к использованию. 4️⃣f-string >>> name = 'World' >>> print(f'Hello {name}') 'Hello World' Относительно новый и удобный по синтаксису метод. Работает быстрей чем format(). Поддерживает аналогичные фишки форматирования (но не все). Еще развивается и обновляется в новых версиях Python. Рекомендован. 5️⃣ string.Template templ = string.Template('Hello $name') print(templ.substitute(name='World')) 'Hello World' Самый медленный, но самый безопасный способ собрать строку. Он в 10 раз медленней самого медленного способа. Но никаких инлайн-экспрешенов или фигурных скобочек. __________ Есть еще ряд других модулей, такие как textwrap, jinja или собственные методы строки. Но данный пост о простых способах вставки переменных в строку. #tricks

447 views

Hashtags

Објавено 3 фев.

Все мы знаем, что в Python всё является объектом. Это значит, что всё можно сохранить в переменную, передать аргументом или вернуть из функции через return. Но известно ли вам, что объектом можно сделать даже срез списка?! То есть сохранить в переменную алгоритм среза и применить его позже. Это можно сделать с помощью builtin функции slice(). Для примера возмем простой список >>> array = list(range(10)) Теперь создадим несколько срезов >>> half = slice(None, len(array)//2) >>> step_by_2 = slice(None, None, 2) >>> invert = slice(None, None, -1) Используется очень просто >>> array[half] [0, 1, 2, 3, 4] >>> array[step_by_2] [0, 2, 4, 6, 8] >>> array[invert] [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] #tricks

424 views

Hashtags

Објавено 1 фев.

🥚 Забавная пасхалка Попытка заглянуть в будущее и узнать, появятся ли в Python фигурный скобки даёт вполне чёткий ответ from __future__ import braces File "<input>", line 1 SyntaxError: not a chance

436 views

Објавено 30 јан.

У словаря есть полезный метод get() который может "аккуратно" спросить значение по ключу и вернуть что-то по умолчанию если такого ключа не нашлось. Я встречал два способа записать эту логику, очень похожие но имеющие серьезную разницу. По умолчанию, если ключа нет в словаре, метод возвращает None или то что указано в аргументе default. >>> my_dict.get('unknown_key', default=123) 123 >>> my_dict.get('unknown_key') None Нас интересует первый вариант. Его можно записать еще и таким способом >>> my_dict.get('unknown_key') or 123 Чем он отличается от варианта с аргументом default? На первый взгляд ничем. Если ключ не существует, то вернется None. Сработает оператор or и мы получим значение 123. Но основная опасность кроется в операторе or! Дело в том, что значение из аргумента default вернется только если КЛЮЧ ОТСУТСТВУЕТ В СЛОВАРЕ. Если ключ найден, то вернется его значение. Если же мы пишем вариантом с or, то правила меняется. Значение 123 мы получим если ключ отсутствует в словаре или если найденное значение равно False в виде bool. Например, если ключ всё же был найден но значение 0, мы всё равно получим 123, несмотря на то, что 0 может быть вполне валидным значением. >>> d = {"key": 0} >>> d.get("key", 5) 0 >>> d.get("key") or 5 5 # во втором случае неоднозначный результат ⚠️ Будьте внимательны! Точно представляйте, что вы хотите получить от своего кода. #tricks

518 views

Hashtags

Објавено 28 јан.

Дополнение к посту про shell в subprocess. Чем полезен режим вызова через shell? То есть, когда вы ставите аргумент shell=True. Ваша команда запустится не напрямую, а через системный шел. А это значит что доступны все возможности шела. Например: - распаковка пути с символом "~" subprocess.check_output('ls ~/', shell=True) - распаковка переменных окружения subprocess.check_output('ls $HOME', shell=True) - использование пайпа команд subprocess.check_output('cat $HOME/output.log | grep -n error', shell=True)) В общем, те, кто активно использует терминал, могут остальное додумать сами😉 #tricks

446 views

Hashtags

12•••10•••20•••2829303132
ПретходнаСтраница 30 од 32Следна