#python #functional-programming
#python #функциональное программирование
Вопрос:
В Mathematica можно записать Map[f,list] как f/@list, где /@ — операторная форма Map . В python есть map(f, list), но есть ли аналогичная операторная форма или пакет, предоставляющий это?
Приложение заключается в том, что глубоко вложенные преобразования с использованием многих карт заканчиваются множеством скобок, тогда как цепочка операторов может быть проще для чтения (и ввода).
Комментарии:
1. Нет, есть только функция. Рассмотрите возможность использования, например, понимания списка для написания более читаемого кода и / или извлечения функций с правильными именами.
Ответ №1:
Простого способа сделать это нет. Python не предоставляет никакого способа определения пользовательских операторов, а набор операторов, которые он предоставляет, довольно стандартен и в основном используется для таких вещей, как числа и строки. map
Объект не поддерживает ничего подобного, но ничто не мешает вам написать свой собственный класс:
class Mapper:
def __init__(self, f):
self.f = f
def __matmul__(self, other):
return map(self.f, other)
Используется как:
In [3]: list(Mapper(lambda x: x 1) @ [1,2,3,4,5])
Out[3]: [2, 3, 4, 5, 6]
Аналогичным образом вы можете ввести Filter
класс:
class Filter:
def __init__(self, p):
self.p = p
def __matmul__(self, other):
return filter(self.p, other)
Используется как:
In [5]: list(Filter(lambda x: x%2==0) @ range(10))
Out[5]: [0, 2, 4, 6, 8]
И на самом деле вы можете видеть, что все классы такого рода практически идентичны, поэтому вы можете их обобщить.
Примечание: @
поскольку оператор является новым для python3.5.
Одна из проблем с использованием этого заключается в том, что @
оно остается ассоциативным, что означает, что вы не можете создавать эти функции. Вы можете использовать что-то вроде этого, чтобы легко составлять их **
:
class Filter:
def __init__(self, p):
self.p = p
def __pow__(self, other):
return filter(self.p, other)
class Mapper:
def __init__(self, f):
self.f = f
def __pow__(self, other):
return map(self.f, other)
Которые позволяют:
In [13]: Filter(lambda x: x%2==0) ** Mapper(lambda x: x 1) ** range(10)
Out[13]: <filter at 0x7fe0696bcd68>
Для полноты картины: вот реализация, которая обобщает эту концепцию, а также работает с @
комбинированием преобразований:
class Apply:
def __init__(self, f):
self.f = f
def __matmul__(self, seq_or_apply):
if isinstance(seq_or_apply, Apply):
return Apply(lambda seq: self.f(seq_or_apply.f(seq)))
return self.f(seq_or_apply)
class Mapper(Apply):
def __init__(self, f):
super().__init__(lambda x: map(f, x))
class Filter(Apply):
def __init__(self, p):
super().__init__(lambda x: filter(p, x))
from functools import reduce
class Reduce(Apply):
def __init__(self, op, init):
super().__init__(lambda seq: reduce(op, seq, init))
Используется как:
In [26]: import operator as op
In [27]: Reduce(op.add, -7) @ Filter(lambda x: x%2==0) @ Mapper(lambda x: x 1) @ range(10)
Out[27]: 23
Комментарии:
1. Это использует ** так, как я раньше не видел. Я так понимаю, это не обычное использование в качестве распаковщика ключевых слов-аргументов?
2. @Ymareth
**
вот оператор возведения в степень, например:2**3 == 8
. Как вы можете видеть, последний класс определяет специальный метод__pow__
, который вызывается, когда**
оператор применяется к некоторому объекту. В этом случае он применяетmap
orfilter
к правому операнду.3. @Ymareth Мне пришлось использовать его, потому что это, AFAIK, единственный ассоциативный оператор с правой ассоциацией, т. Е.
2 ** 2 ** 3 == 2 ** (2 ** 3)
И нет(2 ** 2) ** 3
. Это важно для приведенного выше кода, потому что нам нужна группировкаFilter(...) ** (Mapper(...) ** input)
, а не группировка(Filter(..) ** Mapper(..)) ** input
, которая вызовет ошибку (это то, что происходит с@
).4. @Ymareth Я пошел дальше и добавил обобщение, которое также работает с использованием
@
компиляции функций (хотя и не тестировалось много, и у него могут быть недостатки …)