Как умножить функции в Python?

def sub3(n):
    return n - 3

def square(n):
    return n * n

Составлять функции на python очень просто:

>>> my_list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [square(sub3(n)) for n in my_list]
[9, 4, 1, 0, 1, 4, 9, 16, 25, 36]

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

>>> sorted(my_list, key=lambda n: square(sub3(n)))
[3, 2, 4, 1, 5, 0, 6, 7, 8, 9]

На самом деле это должно быть просто sorted(my_list, key=square*sub3), потому что, черт возьми, функция __mul__ в любом случае не используется ни для чего другого:

>>> square * sub3
TypeError: unsupported operand type(s) for *: 'function' and 'function'

Что ж, давай тогда просто определимся!

>>> type(sub3).__mul__ = 'something'
TypeError: can't set attributes of built-in/extension type 'function'

D'oh!

>>> class CoolerFunction(types.FunctionType):
...     pass
...
TypeError: Error when calling the metaclass bases
    type 'function' is not an acceptable base type

D'oh!

class Hack(object):
    def __init__(self, function):
        self.function = function
    def __call__(self, *args, **kwargs):
        return self.function(*args, **kwargs)
    def __mul__(self, other):
        def hack(*args, **kwargs):
            return self.function(other(*args, **kwargs))
        return Hack(hack)

Эй, теперь мы кое-что получим ...

>>> square = Hack(square)
>>> sub3 = Hack(sub3)
>>> [square(sub3(n)) for n in my_list]
[9, 4, 1, 0, 1, 4, 9, 16, 25, 36]
>>> [(square*sub3)(n) for n in my_list]
[9, 4, 1, 0, 1, 4, 9, 16, 25, 36]
>>> sorted(my_list, key=square*sub3)
[3, 2, 4, 1, 5, 0, 6, 7, 8, 9]

Но мне не нужен Hack вызываемый класс! Правила области видимости совершенно разные, и я не совсем понимаю их, и это, возможно, даже уродливее, чем «ламеда». Я хочу исправить функции. Как я могу это сделать?


person wim    schedule 12.05.2015    source источник
comment
никогда не видели ничего подобного, пробовали ли вы использовать вместо этого partials, похожее на Hack, но, возможно, немного лучше   -  person dashesy    schedule 12.05.2015
comment
@MalikBrahimi - это не композиция функций, чего хочет WIM. en.wikipedia.org/wiki/Function_composition   -  person Jay Kominek    schedule 12.05.2015
comment
В списке рассылки python-idea есть длинный поток по добавлению композиции функций (с использованием предстоящего оператора умножения матриц @, поскольку композиция функций больше похожа на умножение матриц, чем на обычное умножение). Вкратце, однако, это не произойдет в ближайшее время, если вообще произойдет.   -  person chepner    schedule 12.05.2015
comment
@chepner у вас есть ссылка на ветку?   -  person wim    schedule 12.05.2015
comment
Вот ссылка на архивы за май. Это немного беспорядочно, поскольку начальное сообщение, который запустил поток, не имел темы и завершился как 2 или 3 параллельных потока. Одна из наиболее интересных идей для всплывающих окон (IMO) заключалась в том, чтобы вместо фактического составления функций просто сделать кортеж функций вызываемым, чтобы (f, g, h)(x) == f(g(h(x))).   -  person chepner    schedule 12.05.2015
comment
Так или иначе вам понадобится умножать функции после передачи аргумента либо в виде функции, либо в виде отдельной функции.   -  person Malik Brahimi    schedule 12.05.2015
comment
Ой! Идея кортежа звучит круто.   -  person wim    schedule 12.05.2015
comment
Все, что избавляет от (стеки (of (parens (to (баланс!)))))   -  person wim    schedule 12.05.2015
comment
Ненавижу это говорить, но я не нахожу ничего плохого в lambda. Хотя вызываемые кортежи было бы круто.   -  person Mark Ransom    schedule 12.05.2015
comment
Вы можете создать декоратор, который делает функцию компонуемой, заставляя его заключать вашу функцию, которую нужно составить, в вызываемый класс, а затем переопределять метод __mul__; увы, это было бы немного работы и размахивания руками, поскольку вы не можете напрямую создать подкласс function или FunctionType ...   -  person a p    schedule 12.05.2015


Ответы (4)


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

Нравится:

class Composable(object):
    def __init__(self, function):
        self.function = function
    def __call__(self, *args, **kwargs):
        return self.function(*args, **kwargs)
    def __mul__(self, other):
        @Composable
        def composed(*args, **kwargs):
            return self.function(other(*args, **kwargs))
        return composed
    def __rmul__(self, other):
        @Composable
        def composed(*args, **kwargs):
            return other(self.function(*args, **kwargs))
        return composed

Затем вы можете украсить свои функции следующим образом:

@Composable
def sub3(n):
    return n - 3

@Composable
def square(n):
    return n * n

И составьте их так:

(square * sub3)(n)

По сути, это то же самое, что вы сделали, используя свой класс взлома, но используя его как декоратор.

person Jazzer    schedule 12.05.2015
comment
Аккуратный. Я сделал небольшое улучшение, поэтому теперь композиция работает с любыми другими вызываемыми объектами, например (sub3*int)("10") --> 7 и (str*sub3)(10) --> '7'. - person wim; 13.05.2015

Python не имеет (и, вероятно, никогда не будет) поддерживать композицию функций ни на синтаксическом уровне, ни в качестве стандартной библиотечной функции. Существуют различные сторонние модули (такие как функционал), которые обеспечивают функция, реализующая композицию функций.

person chepner    schedule 12.05.2015

Может быть, примерно так:

class Composition(object):
    def __init__(self, *args):
        self.functions = args

    def __call__(self, arg):
        result = arg
        for f in reversed(self.functions):
            result = f(result)

        return result

А потом:

sorted(my_list, key=Composition(square, sub3))
person pavel_form    schedule 12.05.2015
comment
Почему бы вместо этого не использовать закрытие? - person Veedrac; 13.05.2015
comment
Закрытие тоже нормально. Я не вижу большой разницы между этими подходами (за исключением того, что с классом вы можете изменять список функций после создания композиции). - person pavel_form; 13.05.2015

Вы можете составлять функции с помощью библиотеки SSPipe:

from sspipe import p, px

sub3 = px - 3
square = px * px
composed = sub3 | square
print(5 | composed)
person mhsekhavat    schedule 07.06.2019