Пингвитон
151 subscribers
4 photos
12 links
иногда пишу тут какие-то полезные штуки про Linux и Python
Download Telegram
Увидел у Мэтта Годболта в докладе хорошо сформулированное, и с вами поделюсь.
А у нас снова рубрика #ненадотак

Я регулярно сравниваю какие-то хеши. И регулярно я делаю это глазами, сравнивая несколько первых и последних символов. Если они совпадают, всё ок, хеши одинаковые, едем дальше.

Так вот. Не надо так (: Следите:

~$ echo -n "subtitle illusive planes" | md5sum
4188d4cdcf2be92a112bdb8ce4500243 -
~$ echo -n "wantings premises forego" | md5sum
4188d209a75e1a9b90c6fe3efe300243 -


Будьте бдительны, сравнивайте хеши полностью, а не только начало-конец.

Для наглядности картинкой:
Пример из книги The Joy of Cryptography by Mike Rosulek.
Если у вас python3.8+, вы дебажитесь и вам нужно распечатать значения нескольких переменных, то можно в f-string использовать знак =, будет вот так:

>>> a = 'boo'
>>> b = 'foo'
>>> print(f'{a=} {b=} {a.upper()=}')
a='boo' b='foo' a.upper()='BOO'


Обратите внимание, что если вызывать с upper(), то в принте будет указан a.upper(). Удобно сравнивать результаты вызова разных функций или референс и результат обработки: не нужно создавать дополнительные переменные.
Перескажу свежий твит Раймонда Хеттингера, чтобы вы на него подписались.

Объявляя константы с длинным числовым значением, разделяйте их андерскором, чтобы, например, не вглядываться в количество нулей:

>>> c = 1_000_000_000
>>> c
1000000000


Когда дебажитесь и выводите на печать длинные числа, облегчайте себе жизнь им же:

>>> c = 49581370
>>> f'{c:_d}'
'49_581_370'


А Раймонда читать тут.
Решаю задачи на Leetcode, узнаю полезные мелочи, делюсь с вами.

Известное: встроенный zip "склеивает" два итератора в финальный с длиной равной самому короткому итератору:

>>> list(zip('aa', 'bbb'))
[('a', 'b'), ('a', 'b')]


Но я хочу в финальном результате "хвост" и от длинного итератора. А пустое место заполнить каким-то дефолтным значением. В itertools есть zip_longest с параметром fillvalue. Результат будет длиной самого длинного итератора, а значения, которых не хватает, заполнятся из fillvalue. Мне неясно, почему было не сделать это функционалом zip и почему это вытащили в itertools, но как есть (:

>>> from itertools import zip_longest
>>> list(zip_longest('aa', 'bbb', fillvalue=''))
[('a', 'b'), ('a', 'b'), ('', 'b')]


Известно: у списка есть метод insert(idx, value), который вставляет перед указанным индексом idx значение value:

>>> l = [1, 2, 3]
>>> l.insert(0, 'a')
>>> l
['a', 1, 2, 3]


Обнаружила, что insert может работать как append() в случае, если указанного индекса в списке ещё нет. Ожидала, что развалится с IndexError, но нет, просто вставляет значение в конец, очень круто:

>>> l = []
>>> l.insert(0, 1)
>>> l
[1]
>>> l.insert(4, 10)
>>> l
[1, 10]
В модуле operator содержатся функции соответствующие операторам.
Например, operator.eq для == или operator.mul для умножения. Зачем? Чтобы не писать страшненькие лямбды (: Например, с помощью functools.reduce() я хочу получить результат перемножения всех элементов списка.

С лямбдой запись будет такая:

>>> from functools import reduce
>>> reduce(lambda x, y: x * y, [1,2,3])
6


А с operator.mul такая:

>>> from functools import reduce
>>> from operator import mul
>>> reduce(mul, [1,2,3])
6


Тут полный список соответствия операторов функциям.
Какой чудесный способ выбрать min/max из словаря, когда нужно получить только ключ:

>>> d = {'a': 20, 'b': 5, 'c': 30}
>>> min(d, key=d.get)
'b'
>>> max(d, key=d.get)
'c'


Я бы раньше делала через d.items() и key=lambda x: x[1] (% А тут очень красиво, лаконично, понятно.
Начали терзать сомнения, а не будет ли обращение к словарю с помощью get() на каждый элемент сильно медленнее, чем сразу сконструировать список пар с помощью items() и пройтись по нему. Оказалось, что нет (в который раз рекламирую timeit, пользуйтесь им, он классный):

~$ python -m timeit -s 'd = {i:i for i in range(100000)}' -p 'min(d.items(), key=lambda x: x[1])[0]'
50 loops, best of 5: 7.44 msec per loop
~$ python -m timeit -s 'd = {i:i for i in range(100000)}' -p 'min(d, key=d.get)'
100 loops, best of 5: 3.15 msec per loop

Лямбды такие медленные ): Поход гетом в словарь на каждом элементе быстрее вызова лямбды на каждый элемент в 2.5 раза.

Так что больше не терзаюсь.