Существует ли операторная форма map в python?

#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 or filter к правому операнду.

3. @Ymareth Мне пришлось использовать его, потому что это, AFAIK, единственный ассоциативный оператор с правой ассоциацией, т. Е. 2 ** 2 ** 3 == 2 ** (2 ** 3) И нет (2 ** 2) ** 3 . Это важно для приведенного выше кода, потому что нам нужна группировка Filter(...) ** (Mapper(...) ** input) , а не группировка (Filter(..) ** Mapper(..)) ** input , которая вызовет ошибку (это то, что происходит с @ ).

4. @Ymareth Я пошел дальше и добавил обобщение, которое также работает с использованием @ компиляции функций (хотя и не тестировалось много, и у него могут быть недостатки …)