Как создать функцию, которая применяет функцию к входным данным другой функции в Python?

Я ищу хороший функциональный способ сделать следующее:

def add(x, y):
    return x + y

def neg(x):
    return -x

def c(x, y):
    # Apply neg to inputs for add
    _x = neg(x)
    _y = neg(y)

    return add(_x, _y)

neg_sum = c(2, 2)   # -4

Кажется, это связано с каррированием, но все примеры, которые я могу найти, используют функции, которые имеют только одну входную переменную. Я хотел бы что-то похожее на это:

def add(x, y):
    return x + y

def neg(x):
    return -x

c = apply(neg, add)
neg_sum = c(2, 2)   # -4

Вы хотите apply(g, f) создать новую функцию c, которая применяет функцию g ко всем своим аргументам, а затем передает результаты функции f. Это предполагает, что функции c и f принимают одинаковое количество аргументов (возможно, несколько) и что обе c и f возвращают одно значение. Это правильно? Я чувствую, что могут быть некоторые другие случаи, когда вы хотите построить f(g(h(x)), g(h(y))) или h(f(g(x), g(y))), и для этого может не хватать симметрии. Или, может быть, это так, если вы просто вызываете apply несколько раз, иногда с функциями с одним аргументом.

Matthias Fripp 11.12.2020 02:48
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
223
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Это довольно прямой способ сделать это:

def add(x, y):
    return x + y

def neg(x):
    return -x

def apply(g, f):
    # h is a function that returns
    # f(g(arg1), g(arg2), ...)
    def h(*args):
        return f(*map(g, args))
    return h

# or this:
# def apply(g, f):
#    return lambda *args: f(*map(g, args))

c = apply(neg, add)
neg_sum = c(2, 2)   # -4

Обратите внимание, что когда вы используете *myvar в качестве аргумента в определении функции, myvar становится списком всех полученных аргументов, не являющихся ключевыми словами. И если вы вызываете функцию с *expression в качестве аргумента, то все элементы в expression распаковываются и отправляются в качестве отдельных аргументов в функцию. Я использую эти два поведения, чтобы заставить h принимать неизвестный список аргументов, затем применять функцию g к каждому из них (с map), а затем передавать их все в качестве аргументов f.

Уточняя ваш ответ: как бы вы закодировали функцию компоновки, которая работала бы как для add_neg = compose(add, neg), так и для neg_add = compose(neg,add)?

smed 12.12.2020 15:08

Другой подход, в зависимости от того, насколько расширяемым вам это нужно, заключается в создании объекта, который реализует ваши операторные методы, каждый из которых возвращает один и тот же объект, что позволяет вам объединять операторы в цепочку в произвольном порядке.

Если вы можете справиться с тем, что он всегда возвращает список, возможно, вы сможете заставить его работать.

class mathifier:

    def __init__(self,values):
        self.values = values

    def neg(self):
        self.values = [-value for value in self.values]
        return self

    def add(self):
        self.values = [sum(self.values)]
        return self
         
print (mathifier([2,3]).neg().add().values)

И вы все еще можете получить свою именованную функцию для любого набора связанных функций:

neg_add = lambda x : mathifier(x).neg().add()

print(neg_add([2,3]).values)

Из ответа Маттиаса Фриппа я спросил себя: я хотел бы сочинять add и neg в обоих направлениях: add_neg(*args) и neg_add(*args). Это требует немного взломать предложение Матиаса. Идея состоит в том, чтобы получить некоторый намек на арность (количество аргументов) функций для составления. Эта информация получена с небольшим самоанализом благодаря модулю inspect. Имея это в виду, мы адаптируем способ передачи аргументов через цепочку функций. Основное предположение здесь состоит в том, что мы имеем дело с реальными функциями в математическом смысле, то есть с функциями, возвращающими ОДНО число с плавающей точкой и принимающими по крайней мере один аргумент.

from functools import reduce
from inspect import getfullargspec


def arity_one(func):
    spec = getfullargspec(func)
    return len(spec[0])==1 and spec[1] is None

def add(*args):
    return reduce(lambda x,y:x+y, args, 0)

def neg(x):
    return -x

def compose(fun1,fun2):
    def comp(*args):
        if arity_one(fun2): return fun1(*(map( fun2, args)))
        else: return fun1(fun2(*args))
    return comp


neg_add = compose(neg, add)
add_neg = compose(add, neg)

print(f"-2+(-3) = {add_neg(2, 3)}")
print(f"-(2+3) = {neg_add(2, 3)}")

Решение все еще очень adhoc...

Другие вопросы по теме