Це мій другий пост про освоєння Python 3. Почався він з того, що захотілось мені використати всім відому вбудовану функцію reduce, а я замість робочого коду отримав NameError. Виявляється в Python 3 вона вже не вбудована, а знаходится в модулі functools, в який, починаючи з версії Python 2.5, засунули декілька корисних речей для роботи з об'єктами-функціями. Тобто тепер функцію reduce потрібно імпортувати.
from functools import reduce
Варто зазначити, що специфікація функції не змінилась, працює вона точно так, як і в другому пітоні. Постало питання: "Навіщо?". (Більш детально про reduce читаємо в документації).
З чого все почалось?
А почалось все з Гвідо ван Россума, який сказав наступне, коли тільки Python 3k починали розробляти. Ось довільний переклад:
Також відомо, що Гвідо вважає ці речі непотрібними, оскільки є так звані "list comprehensions", тобто конструкції типу:
>>> [i * 2 for i in my_list if i > 0]
Ось думка "великодушного диктатора" про reduce:
Чи потрібна reduce взагалі?
Глянувши свій код, бачу, що за 3 з гаком роки роботи з Python я використовував reduce, навідміну від map, filter та lambda, дуже рідко. Задумуючись про різні способи реалізації того чи іншого блоку коду, можна побачити масу випадків, де потрібно застосувати reduce, і в більшості з них знаходяться альтернативні рішення, які роблять код більш зрозумілим і доступнішим. Розглянемо декілька прикладів на простому списку:
>>> v = [0,1,2,3,4]
Додавання
>>> r = reduce(lambda x, y: x + y, v)>>> print(r)
10
Звісно, таке нікому не потрібно, коли є sum:
>>> 10
Розглянемо множення
>>> v = [1, 2, 3, 4]
>>> reduce(lambda x, y: x * y, v)
>>> 24
>>> r = 1
>>> for i in v:
>>> r *= i
>>> 24
Тут варіант з reduce виглядає більш ніж привабливим.
Об'єднання списків:
>>> reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])
[1, 2, 3, 4, 5, 6, 7, 8]
>>> from itertools import chain
>>> list(chain([1, 2, 3], [4, 5], [6, 7, 8]))
[1, 2, 3, 4, 5, 6, 7, 8]
По-моєму, варіант з itertools є більш зрозумілим і, що набагато цікавіше, повертає не список, а ... здогадайтесь самі. Для інших задач часто знаходяться більш елегантні, чи більш доступні рішення, наприклад, для логічних - використання функцій any та all.
reduce для мене та висновок
Деколи з використанням reduce помічаю, що через деякий час код сприймаєтся важче, особливо, якщо це - не тривіальне використання оператора, а reduce, намішана з багатьма вкладеними викликами функцій, тому стараюсь застосовувати reduce там, де це дійсно спрощує код і робить його доступнішим, а такі ситуації, по-моєму, виникають дуже рідко і повинні бути на виду зразу: якщо вже задумуєшся, значить щось не так, щось некрасиво.
Взагалі-то я підтримую рішення винести reduce з розряду вбудованих функцій в модуль functools, тепер, перед тим як нагадити в коді - двічі подумаю, ну для цього ж потрібно ще один додатковий імпорт! :)
Всім дякую за увагу...
Почему 1*2*3*4 = 48 ?
ВідповістиВидалитиIsem, спасибо за поправку, наверное не из того терминала скопипастил =(.
ВідповістиВидалитиСлово-то какое ... терминал .. в винде =)
Раз теперь исправлено, надо и мой комментарий исправить на:
ВідповістиВидалитиПочему 1*2*3*4 = 24 ?
:)
Я думаю, что вариант с
ВідповістиВидалитиproduct( v ) будет выглядеть еще более привлекательным.