Есть ли у кого-нибудь здесь полезный код, который использует функцию reduce () в python? Есть ли какой-нибудь другой код, кроме обычных + и *, который мы видим в примерах?
Обратитесь к Судьба reduce () в Python 3000 от GvR






Не уверен, что это то, что вам нужно, но вы можете искать исходный код в Google.
Перейдите по ссылке для поиска 'функция: уменьшить () язык: питон' в поиске Google Code
На первый взгляд, следующие проекты используют reduce()
и т.д. и т.п., но в этом нет ничего удивительного, поскольку это огромные проекты.
Функциональность сокращения может быть реализована с помощью рекурсии функций, которую, я думаю, Гвидо считал более явной.
Обновлять:
Поскольку поиск кода Google был прекращен 15 января 2012 года, помимо возврата к обычному поиску в Google, есть кое-что под названием Коллекция фрагментов кода, которое выглядит многообещающим. В ответах на этот (закрытый) вопрос Замена для Google Code Search? упоминается ряд других ресурсов.
Обновление 2 (29 мая 2017 г.):
Хорошим источником примеров Python (в открытом коде) является Поисковая система Nullege.
«Функциональность сокращения может быть реализована с помощью рекурсии функций» ... Или цикла for.
Кроме того, поиск reduce () приводит к появлению проектов, которые определяют функции сокращения в своем коде. Вы должны искать lang: python "reduce (", чтобы найти фактическое использование встроенной функции.
@Seun Osewa: Даже поиск lang:python "reduce(" найдет определения reduce в зависимости от стиля кодирования исходного кода.
У меня есть старая реализация Python трубка, которая использует сокращение и модуль glob для создания списка файлов для обработки:
files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))
В то время мне это показалось удобным, но на самом деле в этом нет необходимости, так как что-то подобное так же хорошо и, вероятно, более читабельно.
files = []
for f in args:
files.extend(glob.glob(f))
Как насчет понимания списка? Кажется, это идеальное приложение для него: files = [glob.glob(f) for f in args]
На самом деле, @steveha, ваш пример приведет к списку списков расширенных глобусов, а не к плоскому списку всех элементов, которые соответствуют глобусам, но вы можете использовать понимание списка + сумма, как @ [Eli Courtwright] (# 16198 ) указывает на то.
Хорошо, вы правы, извините за это. Мне все еще очень не нравится комбинация extension / reduce / lambda / map! Я бы порекомендовал импортировать itertools, используя рецепт flatten() из docs.python.org/library/itertools.html, а затем написать: files = flatten(glob.glob(f) for f in args) (И на этот раз я протестировал код перед его отправкой, и я знаю, что это работает правильно).
files = chain.from_iterable(imap(iglob, args)), где chain, imap из модуля itertools, а glob.iglob полезен, если шаблон из args может давать файлы из нескольких каталогов.
@Blair Conrad: Вы также можете реализовать свой glob / reduce, используя сумму, например:
files = sum([glob.glob(f) for f in args], [])
Это менее многословно, чем в любом из ваших двух примеров, идеально подходит для Python и по-прежнему представляет собой только одну строку кода.
Итак, чтобы ответить на исходный вопрос, я лично стараюсь избегать использования reduce, потому что в этом нет необходимости, и я считаю, что это менее понятно, чем другие подходы. Однако некоторые люди привыкли сокращать и предпочитают перечислять понимания (особенно программисты на Haskell). Но если вы еще не думаете о проблеме с точки зрения сокращения, вам, вероятно, не нужно беспокоиться об ее использовании.
После greping моего кода кажется, что единственное, что я использовал для сокращения, - это вычисление факториала:
reduce(operator.mul, xrange(1, x+1) or (1,))
Затем замените его на math.factorial docs.python.org/2/library/math.html#math.factorial
Использование reduce, которое я обнаружил в своем коде, связано с ситуацией, когда у меня была некоторая структура классов для логического выражения, и мне нужно было преобразовать список этих объектов выражения в соединение выражений. У меня уже была функция make_and для создания конъюнкции по двум выражениям, поэтому я написал reduce(make_and,l). (Я знал, что список не пустой; иначе это было бы что-то вроде reduce(make_and,l,make_true).)
Именно по этой причине (некоторые) функциональные программисты любят reduce (или функции складывать, как такие функции обычно называются). Часто уже есть много двоичных функций, таких как +, *, min, max, конкатенация и, в моем случае, make_and и make_or. Наличие reduce делает тривиальным преобразование этих операций в списки (или деревья, или что-то еще, для функций сворачивания в целом).
Конечно, если часто используются определенные экземпляры (например, sum), вы не хотите продолжать писать reduce. Однако вместо того, чтобы определять sum с помощью некоторого цикла for, вы так же легко определяете его с помощью может с помощью reduce.
Читаемость, как упоминалось другими, действительно является проблемой. Однако вы можете возразить, что единственная причина, по которой люди находят reduce менее «понятным», заключается в том, что это не та функция, которую многие люди знают и / или используют.
для защиты от пустого списка вы можете использовать короткое замыкание оператора and: L and reduce(make_and, L), если в этом случае уместен возврат пустого списка
Другие применения, которые я нашел для него, помимо + и *, были с и и или, но теперь у нас есть any и all для замены этих случаев.
foldl и foldr действительно часто встречаются в схеме ...
Вот несколько милых приемов:
Свести список
Цель: превратить [[1, 2, 3], [4, 5], [6, 7, 8]] в [1, 2, 3, 4, 5, 6, 7, 8].
reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])
Список цифр в число
Цель: превратить [1, 2, 3, 4, 5, 6, 7, 8] в 12345678.
Уродливый, медленный путь:
int("".join(map(str, [1,2,3,4,5,6,7,8])))
Довольно reduce способ:
reduce(lambda a,d: 10*a+d, [1,2,3,4,5,6,7,8], 0)
Почему вы добавили ", 0" в последнем примере? Чтобы иметь возможность справиться с появлением пустого списка, или есть другие причины?
Для сглаживания списка я предпочитаю list (itertools.chain (* nested_list))
@conny: я думаю, вам всегда нужен начальный элемент. в данном случае ti равно 0. В противном случае, что было бы при взгляде на первый элемент?
когда начальное значение не задано, сокращение просто применяет сокращение к первым элементам два. если не задано начальное значение и есть только один элемент, то сокращения не происходит .... я думаю ...
@ Роберто: Это не обрабатывает вложенные списки.
@Daenyth: Решение reduce тоже не работает!
сумма ([[1, 2, 3], [4, 5], [6, 7, 8]], [])
Ужасный фрагмент кода ... Лучшее, что сделал GvR, - это убрал сокращение из встроенных команд.
@ Роберто Бонвалле: list(itertools.chain.from_iterable(nested_list))
Это также полезно для побитовых операций. Что, если вы хотите использовать побитовые числа или связку чисел, например, если вам нужно преобразовать флаги из списка в битовую маску?
Вы называете последний пример сокращения красивым? ИМХО, это совсем не красиво. Для меня int ("". Join (str (i) for i in range (1, 9))) гораздо более читабелен.
@VictorYan: в какой-то момент это дело вкуса. но способ reduce намного более эффективен, по крайней мере, алгоритмически (возможно, накладные расходы на выполнение вычислений с помощью лямбда больше, чем при использовании другого способа. Необходимо провести сравнительный анализ).
Выполняя некоторые тесты, «уродливый» способ быстрее подходит для больших списков. timeit.repeat('int("".join(map(str, digit_list)))', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000) занимает ~ 0,09 секунды, а timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000) - 0,36 секунды (примерно в 4 раза медленнее). Обычно умножение на 10 становится дорогостоящим, когда список становится большим, в то время как int в str и конкатенация остаются дешевыми.
Конечно, да, для небольших списков (размер 10) метод уменьшения в 1,3 раза быстрее. Однако даже в этом случае избегание сокращения и выполнение простого цикла выполняется еще быстрее. timeit.repeat('convert_digit_list_to_int(digit_list)', setup = 'digit_list = [d%10 for d in xrange(1,10)]\ndef convert_digit_list_to_int(digits):\n i = 0\n for d in digits:\n i = 10*i + d\n return i', number=100000) занимает 0,06 с, timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,10))', number=100000) - 0,12 с, а преобразование цифр в метод str - 0,16 с.
Для # 2 список цифр для числа. Я бы сделал примерно так: int(''.join([str(i) for i in l])). Немного неудобно, так как мне нужно отбросить его обратно и заставить, но это быстро (понимание списка и str.join). Хорошо, только что видел, как Виктор Ян опубликовал это раньше. то же самое.
@Devy: map(str, l) быстрее, чем понимание списка, но метод уменьшения работает быстрее
@drjimbob: Я получаю аналогичные результаты на pypy: int(''.join быстрее, чем reduce() для N = 1000, а reduce() быстрее, чем int(''.join для N = 10`.
Решение «список цифр» обобщается на преобразование основание (с учетом списка цифровых значений) и на полиномиальную оценку с использованием Метод Хорнера (с учетом списка коэффициентов от высокого к меньшему).
reduce() можно использовать для поиска Наименьшее общее кратное для 3 и более чисел:
#!/usr/bin/env python
from fractions import gcd
from functools import reduce
def lcm(*args):
return reduce(lambda a,b: a * b // gcd(a, b), args)
Пример:
>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560
Что такое lcm во второй строке?
@BirdJaguarIV: следуйте связь в ответе. lcm() возвращает наименьшее общее кратное двух чисел.
reduce() может использоваться для разрешения имен, разделенных точками (где eval() слишком небезопасен для использования):
>>> import __main__
>>> reduce(getattr, "os.path.abspath".split('.'), __main__)
<function abspath at 0x009AB530>
Я пишу функцию составления для языка, поэтому я создаю составную функцию, используя сокращение вместе с оператором применения.
Вкратце, compose принимает список функций, которые нужно объединить в одну функцию. Если у меня есть сложная операция, которая выполняется поэтапно, я хочу собрать все это так:
complexop = compose(stage4, stage3, stage2, stage1)
Таким образом, я могу применить его к такому выражению:
complexop(expression)
И я хочу, чтобы это было эквивалентно:
stage4(stage3(stage2(stage1(expression))))
Теперь, чтобы построить свои внутренние объекты, я хочу, чтобы он сказал:
Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))
(Класс Lambda создает пользовательскую функцию, а Apply создает приложение-функцию.)
Теперь уменьшите, к сожалению, неправильные складки, поэтому я примерно использовал:
reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))
Чтобы выяснить, что дает сокращение, попробуйте это в REPL:
reduce(lambda x, y: (x, y), range(1, 11))
reduce(lambda x, y: (y, x), reversed(range(1, 11)))
Я использовал compose = lambda *func: lambda arg: reduce(lambda x, f: f(x), reversed(funcs), arg) для генерировать все возможные комбинации функций для тестирования производительности.
Найдите пересечение N заданных списков:
input_list = [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7]]
result = reduce(set.intersection, map(set, input_list))
возвращает:
result = set([3, 4, 5])
через: Python - пересечение двух списков
Вы можете заменить value = json_obj['a']['b']['c']['d']['e'] на:
value = reduce(dict.__getitem__, 'abcde', json_obj)
Если у вас уже есть путь a/b/c/.. в виде списка. Например, Изменять значения в dict вложенных dicts, используя элементы в списке.
Я думаю, что сокращение - глупая команда. Следовательно:
reduce(lambda hold,next:hold+chr(((ord(next.upper())-65)+13)%26+65),'znlorabggbbhfrshy','')
Мне тоже нравится ирония здесь
Reduce не ограничивается скалярными операциями; его также можно использовать для сортировки вещей по ведрам. (Это то, что я использую чаще всего).
Представьте себе случай, когда у вас есть список объектов, и вы хотите реорганизовать его иерархически на основе свойств, хранящихся в объекте. В следующем примере я создаю список объектов метаданных, связанных со статьями в газете с кодировкой XML, с помощью функции articles. articles генерирует список элементов XML, а затем отображает их один за другим, создавая объекты, которые содержат некоторую интересную информацию о них. Во внешнем интерфейсе я хочу, чтобы пользователь мог просматривать статьи по разделам / подразделам / заголовкам. Поэтому я использую reduce, чтобы взять список статей и вернуть единственный словарь, который отражает иерархию разделов / подразделов / статей.
from lxml import etree
from Reader import Reader
class IssueReader(Reader):
def articles(self):
arts = self.q('//div3') # inherited ... runs an xpath query against the issue
subsection = etree.XPath('./ancestor::div2/@type')
section = etree.XPath('./ancestor::div1/@type')
header_text = etree.XPath('./head//text()')
return map(lambda art: {
'text_id': self.id,
'path': self.getpath(art)[0],
'subsection': (subsection(art)[0] or '[none]'),
'section': (section(art)[0] or '[none]'),
'headline': (''.join(header_text(art)) or '[none]')
}, arts)
def by_section(self):
arts = self.articles()
def extract(acc, art): # acc for accumulator
section = acc.get(art['section'], False)
if section:
subsection = acc.get(art['subsection'], False)
if subsection:
subsection.append(art)
else:
section[art['subsection']] = [art]
else:
acc[art['section']] = {art['subsection']: [art]}
return acc
return reduce(extract, arts, {})
Я привожу здесь обе функции, потому что считаю, что это показывает, как map и reduce могут хорошо дополнять друг друга при работе с объектами. То же самое можно было бы сделать с помощью цикла for, ... но серьезное времяпровождение с функциональным языком заставляло меня думать в терминах сопоставления и сокращения.
Кстати, если у кого-нибудь есть лучший способ установить свойства, как я делаю в extract, где родители свойства, которое вы хотите установить, могут еще не существовать, сообщите мне.
reduce можно использовать для получения списка с максимальным n-м элементом
reduce(lambda x,y: x if x[2] > y[2] else y,[[1,2,3,4],[5,2,5,7],[1,6,0,2]])
вернет [5, 2, 5, 7], поскольку это список с максимальным 3-м элементом +
max (lst, key = лямбда x: x [2])
Состав функций: если у вас уже есть список функций, которые вы хотите применить последовательно, например:
color = lambda x: x.replace('brown', 'blue')
speed = lambda x: x.replace('quick', 'slow')
work = lambda x: x.replace('lazy', 'industrious')
fs = [str.lower, color, speed, work, str.title]
Затем вы можете применить их все последовательно с помощью:
>>> call = lambda s, func: func(s)
>>> s = "The Quick Brown Fox Jumps Over the Lazy Dog"
>>> reduce(call, fs, s)
'The Slow Blue Fox Jumps Over The Industrious Dog'
В этом случае цепочка методов может быть более читаемой. Но иногда это невозможно, и такая композиция может быть более читаемой и удобной, чем синтаксис типа f1(f2(f3(f4(x)))).
Преимущество состоит в том, что вы можете изменить список функций, применяемых в коде.
Использование reduce (), чтобы узнать, является ли список дат последовательным:
from datetime import date, timedelta
def checked(d1, d2):
"""
We assume the date list is sorted.
If d2 & d1 are different by 1, everything up to d2 is consecutive, so d2
can advance to the next reduction.
If d2 & d1 are not different by 1, returning d1 - 1 for the next reduction
will guarantee the result produced by reduce() to be something other than
the last date in the sorted date list.
Definition 1: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider consecutive
Definition 2: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider not consecutive
"""
#if (d2 - d1).days == 1 or (d2 - d1).days == 0: # for Definition 1
if (d2 - d1).days == 1: # for Definition 2
return d2
else:
return d1 + timedelta(days=-1)
# datelist = [date(2014, 1, 1), date(2014, 1, 3),
# date(2013, 12, 31), date(2013, 12, 30)]
# datelist = [date(2014, 2, 19), date(2014, 2, 19), date(2014, 2, 20),
# date(2014, 2, 21), date(2014, 2, 22)]
datelist = [date(2014, 2, 19), date(2014, 2, 21),
date(2014, 2, 22), date(2014, 2, 20)]
datelist.sort()
if datelist[-1] == reduce(checked, datelist):
print "dates are consecutive"
else:
print "dates are not consecutive"
Допустим, есть некоторые годовые статистические данные, хранящиеся в списке счетчиков. Мы хотим найти значения MIN / MAX для каждого месяца в разные годы. Например, для января это будет 10. А для февраля - 15. Нам нужно сохранить результаты в новом счетчике.
from collections import Counter
stat2011 = Counter({"January": 12, "February": 20, "March": 50, "April": 70, "May": 15,
"June": 35, "July": 30, "August": 15, "September": 20, "October": 60,
"November": 13, "December": 50})
stat2012 = Counter({"January": 36, "February": 15, "March": 50, "April": 10, "May": 90,
"June": 25, "July": 35, "August": 15, "September": 20, "October": 30,
"November": 10, "December": 25})
stat2013 = Counter({"January": 10, "February": 60, "March": 90, "April": 10, "May": 80,
"June": 50, "July": 30, "August": 15, "September": 20, "October": 75,
"November": 60, "December": 15})
stat_list = [stat2011, stat2012, stat2013]
print reduce(lambda x, y: x & y, stat_list) # MIN
print reduce(lambda x, y: x | y, stat_list) # MAX
import os
files = [
# full filenames
"var/log/apache/errors.log",
"home/kane/images/avatars/crusader.png",
"home/jane/documents/diary.txt",
"home/kane/images/selfie.jpg",
"var/log/abc.txt",
"home/kane/.vimrc",
"home/kane/images/avatars/paladin.png",
]
# unfolding of plain filiname list to file-tree
fs_tree = ({}, # dict of folders
[]) # list of files
for full_name in files:
path, fn = os.path.split(full_name)
reduce(
# this fucction walks deep into path
# and creates placeholders for subfolders
lambda d, k: d[0].setdefault(k, # walk deep
({}, [])), # or create subfolder storage
path.split(os.path.sep),
fs_tree
)[1].append(fn)
print fs_tree
#({'home': (
# {'jane': (
# {'documents': (
# {},
# ['diary.txt']
# )},
# []
# ),
# 'kane': (
# {'images': (
# {'avatars': (
# {},
# ['crusader.png',
# 'paladin.png']
# )},
# ['selfie.jpg']
# )},
# ['.vimrc']
# )},
# []
# ),
# 'var': (
# {'log': (
# {'apache': (
# {},
# ['errors.log']
# )},
# ['abc.txt']
# )},
# [])
#},
#[])
Не могли бы вы добавить небольшое объяснение того, что здесь происходит? В остальном полезность действительно совсем не очевидна.
У меня есть объекты, представляющие своего рода перекрывающиеся интервалы (геномные экзоны), и я переопределил их пересечение с помощью __and__:
class Exon:
def __init__(self):
...
def __and__(self,other):
...
length = self.length + other.length # (e.g.)
return self.__class__(...length,...)
Затем, когда у меня есть их коллекция (например, в том же гене), я использую
intersection = reduce(lambda x,y: x&y, exons)
reduce может использоваться для поддержки поиска связанных атрибутов:
reduce(getattr, ('request', 'user', 'email'), self)
Конечно, это эквивалентно
self.request.user.email
но это полезно, когда ваш код должен принимать произвольный список атрибутов.
(Связанные атрибуты произвольной длины распространены при работе с моделями Django.)
reduce полезен, когда вам нужно найти объединение или пересечение последовательности объектов, подобных set.
>>> reduce(operator.or_, ({1}, {1, 2}, {1, 3})) # union
{1, 2, 3}
>>> reduce(operator.and_, ({1}, {1, 2}, {1, 3})) # intersection
{1}
(Помимо фактических set, примером их является Объекты Q в Django.)
С другой стороны, если вы имеете дело с bool, вам следует использовать any и all:
>>> any((True, False, True))
True
def dump(fname,iterable):
with open(fname,'w') as f:
reduce(lambda x, y: f.write(unicode(y,'utf-8')), iterable)
Это сбрасывает x и пишет y? Что, если x был важен?
x - результат предыдущей итерации. В данном случае это результат f.write, то есть количество записанных символов. Вряд ли будет полезно. Тем не менее, этот код - просто способ показать, как вы можете использовать сокращение для итерации по итерации и сохранения в файл. Это как for y in iterable: x=f.write(y)
Я понимаю! Но тогда самый первый встреченный x является элементом начальной итерации, а не результатом оператора write? Кроме того, этот код зависит от порядка, в котором reduce встречает свои аргументы, поэтому он не будет работать, если мы заменим pyspark.reduce на functools.reduce?
Я только что нашел полезное использование reduce: разделение строки без удаления разделителя. Код полностью взят из блога Programatic Speaking. Вот код:
reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem == "\n" else acc + [elem], re.split("(\n)", "a\nb\nc\n"), [])
Вот результат:
['a\n', 'b\n', 'c\n', '']
Обратите внимание, что он обрабатывает крайние случаи, которых не делает популярный ответ в SO. Для более подробного объяснения я перенаправляю вас к исходному сообщению в блоге.
Я использовал reduceобъединить список векторов поиска PostgreSQL с оператором || в sqlalchemy-searchchable:
vectors = (self.column_vector(getattr(self.table.c, column_name))
for column_name in self.indexed_columns)
concatenated = reduce(lambda x, y: x.op('||')(y), vectors)
compiled = concatenated.compile(self.conn)
from functools import reduceпозволяет одному и тому же коду работать как на Python 2, так и на Python 3.