Forwarded from Вестник села Маракуйева
Увидел у Мэтта Годболта в докладе хорошо сформулированное, и с вами поделюсь.
Сегодняшняя заметка в итоге вышла настолько толстой, что написала её в Телеграфе. Смотреть, как из вложенного цикла сделать плоский, узнать, что ещё умеет делать sum, кроме складывания чисел, и как замерить время выполнения кода, -- тут.
Telegraph
Как из вложенного цикла сделать плоский, что ещё умеет делать sum, кроме складывания чисел, и как легко замерить время выполнения…
Вкручиваю фичу в проект, наткнулась в коде на возможность использовать sum() неизвестным мне ранее способом. Обычно sum() используется, чтобы сложить числа внутри списка: > sum([1, 2, 3]) 6 На самом деле, вызов этой функции выглядит как: sum([1, 2, 3], start=0)…
А у нас снова рубрика #ненадотак
Я регулярно сравниваю какие-то хеши. И регулярно я делаю это глазами, сравнивая несколько первых и последних символов. Если они совпадают, всё ок, хеши одинаковые, едем дальше.
Так вот. Не надо так (: Следите:
Будьте бдительны, сравнивайте хеши полностью, а не только начало-конец.
Для наглядности картинкой:
Я регулярно сравниваю какие-то хеши. И регулярно я делаю это глазами, сравнивая несколько первых и последних символов. Если они совпадают, всё ок, хеши одинаковые, едем дальше.
Так вот. Не надо так (: Следите:
~$ echo -n "subtitle illusive planes" | md5sum
4188d4cdcf2be92a112bdb8ce4500243 -
~$ echo -n "wantings premises forego" | md5sum
4188d209a75e1a9b90c6fe3efe300243 -
Будьте бдительны, сравнивайте хеши полностью, а не только начало-конец.
Для наглядности картинкой:
Если у вас 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'А Раймонда читать тут.
X (formerly Twitter)
Raymond Hettinger (@raymondh) on X
Chief trainer for Mutable Minds.
Certified Public Accountant, Retired
Python guru. Alloy & TLA⁺ enthusiast.
Aspiring pianist. Former pilot.
Born at 320 ppm CO₂.
Certified Public Accountant, Retired
Python guru. Alloy & TLA⁺ enthusiast.
Aspiring pianist. Former pilot.
Born at 320 ppm CO₂.
Решаю задачи на Leetcode, узнаю полезные мелочи, делюсь с вами.
Известное: встроенный zip "склеивает" два итератора в финальный с длиной равной самому короткому итератору:
Но я хочу в финальном результате "хвост" и от длинного итератора. А пустое место заполнить каким-то дефолтным значением. В itertools есть zip_longest с параметром fillvalue. Результат будет длиной самого длинного итератора, а значения, которых не хватает, заполнятся из fillvalue. Мне неясно, почему было не сделать это функционалом zip и почему это вытащили в itertools, но как есть (:
Известно: у списка есть метод insert(idx, value), который вставляет перед указанным индексом idx значение value:
Обнаружила, что insert может работать как append() в случае, если указанного индекса в списке ещё нет. Ожидала, что развалится с IndexError, но нет, просто вставляет значение в конец, очень круто:
Известное: встроенный 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() я хочу получить результат перемножения всех элементов списка.
С лямбдой запись будет такая:
А с operator.mul такая:
Тут полный список соответствия операторов функциям.
Например, 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, пользуйтесь им, он классный):
Лямбды такие медленные ): Поход гетом в словарь на каждом элементе быстрее вызова лямбды на каждый элемент в 2.5 раза.
Так что больше не терзаюсь.
~$ 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 раза.
Так что больше не терзаюсь.