Комментарии 24
Ну во-первых, далеко не в разы, а всего лишь процентов на 5. Во-вторых — list comprehension работает еще быстрее. Но тоже далеко не в разы, а процентов на 10 быстрее цикла (И соответственно, процентов на 15 быстрее map).
In [10]: l = list(range(1000000))
In [11]: %timeit test_map(l)
346 ms ± 3.94 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [12]: %timeit test_loop(l)
328 ms ± 802 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [13]: %timeit test_list_comprehension(l)
292 ms ± 970 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [14]: %timeit test_generator(l)
313 ms ± 2.44 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [15]: %timeit test_loop2(l)
324 ms ± 411 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [1]: def test_map(l):
...: return list(map(lambda x: x ** 2, l))
...:
In [2]: def test_loop(l):
...: res = []
...: for n in l:
...: res.append(n ** 2)
...: return res
...:
In [3]: def test_list_comprehension(l):
...: return [n**2 for n in l]
...:
In [4]: def test_generator(l):
...: return list((n**2 for n in l))
...:
In [5]: def test_loop2(l):
...: res = [0] * len(l)
...: for i, n in enumerate(l):
...: res[i] = n ** 2
...: return res
...:
- reduce: 5.9973929
- any_gen: 1.0433306
- any_lst: 5.5095213
- loop: 0.5569408000000013
У вас тесты делают разные вещи. Reduce и any_lst пробегают весь список, тогда как в остальных двух тестах есть ранняя остановка.
Так что объективно с циклом можно сравнивать только any_gen. И да в данном случае он работает в ~2 раза медленнее.
Но учитывайте, что оба варианта (и список и генератор) здесь делают в среднем всего лишь ок. 50 итераций на тест. Поэтому накладные расходы на инициализацию генераторов заметно сказываются на времени выполнения. Если проверять на более длинных списках, то никаких "в разы быстрее" уже не останется.
ну хорошо, давайте сделаем такой цикл, а генератор забудем как нерелевантный:
def loop(error, list_of_errors):
a = False
for error_message in list_of_errors:
if error in error_message:
a = True
return a
list items 10, list length 500:
- loop 5.5052784
- reduce 12.4489548
- any_lst 9.775371399999997
list items 5000, list length 10:
- loop 40.1854446
- reduce 53.282886000000005
- any_lst 56.423127699999995
всё равно ощутимо медленнее. оверхед на вызов функции даёт о себе знать.
так-то я только за функциональный подход и сам стараюсь его везде использовать: правильно написанный функциональный код легче читать и поддерживать.
но в преимущественно императивном языке типа питона приходится искать баланс между красотой и производительностью.
баланс между красотой и производительностью.
Я не уверен, что цепочки filter, map, reduce так уж красивы и выразительны в Python. Все же синтаксис Python, изначально императивного языка, активно сопротивляется подобному. Например lambda функции, которые часто используют в фильтрах и мапах, в Python сделаны слишком уж многословными. Или отсутствие в языке встроеного метода композиции. Иной раз приходится на ревью кода плакать кровавыми слезами от попыток в лоб сделать многоступенчатую обработку с кучей лямбд.
{
'pages': [
{
'customerapprovalStatus'
if k == 'statusCA' else k:
'APPROVED'
if k == 'statusCA' else page[k]
for k in page if k == 'pageID' or k == 'statusCA'
}
for page in elements['pages']
]
}
А для построения IMAP-запроса с множественными FROM reduce подходит как нельзя лучше: gist.github.com/zmej-serow/4d6292591c690563e7dd205964f8b8ee
map и filter заменяются генератором списков с условием. А reduce Гвидо хотел выпилить еще 15 лет назад, т.к. если в add_nums что-то сложнее a+b, то сходу трудно понять. Проще вынести в цикл.
Автогенератор таких статей:
import random
def write_part():
item = random.choice(dir(__builtins__))
print(f"# {item}")
print(getattr(__builtins__,item).__doc__)
if __name__ == '__main__':
print("Три объекта питона, о которых вы не знали (наверное)")
write_part()
write_part()
write_part()
Алгоритимический метакопирайтинг.
И да, я не знал про __package__
, memoryview
и __import__
. Забавно.
Гугл-перевод — это конечно хорошо, но нужно его вычитывать перед публикацией. И желательно чтобы это делал человек, который хоть немного понимает, о чем речь в тексте и способен на чуть большее, чем согласование частей речи в предложении.
мы можем заранее объявить функцию, а затем передать ее в «filter()» вместе со списком итераций.
Что такое "список итераций"?
Тут конечно и в оригинале неверная формулировка
and then pass it to filter() alongside the list of iterables.
второй параметр у filter — это не "the list of iterables", а один iterable (частным случаем которого является list).
Но все же в оригинале фраза менее бредова, чем в переводе (прошу заметить, там the list of iterables, а не the list of iterations).
Обратите внимание, что «map()» сама вернет объект карты
Какой карты? Игральной? Географической?
Название функции map происходит от глагола to map, т.е. отображать. И адекватный перевод фразы map object в данном контексте — объект-отображение или что-то в этом духе. Или даже map-объект. Все лучше, чем какие-то карты, непонятно откуда взявшиеся.
И да, я почти не использовал переводчик. Такие ошибки появляются из-за того, что я еще новенький в программировании и просто хотел попробовать себя в этом. Я учту все, что написали здесь в комментариях.
Как я уже писал, по хорошему эту фразу переводить не надо, так как она не верна. Вместо нее стоило бы написать "итерируемый объект (например список)" и добавить примечание что в оригинале ошибка. Если все же переводить дословно, то "список итерируемых объектов".
Если же делаю что-то другое, тот же map() или list comprehension, то пишу коммент, где написано, что тут делается.
Зря. Некоторые вещи очень идиоматично получаются.
for token in map(str.strip, filter(line.split(','))):
....
Оно заменяет собой вот такое:
for raw_token in line.split(','):
if not raw_token:
continue
token = raw_token.strip()
Лишний if
с continue
, лишние переменные без принципиального смысла. Вот если в map зафигачить лямбду с нетривиальным выражением — уже не очень.
Но это вопрос общей культуры.
Сейчас циклы использую только если прям массово нужны side-effects в процессе обработки, или есть коллеги, которым тяжело читать coomprehensions.
И про эффективность не забываем, конечно
В качестве внекласного чтения рекомендую почитать pep на всякие comprehensions, и найти дату, когда reduce вынесли в модуль functools
p.s. аааа!!! pop-202, list comprehension создан в 2000 году…
Топ-3 функции Python, о которых вы не знали (Наверное)