Composing Functions in Python

Composing functions in python

It doesn't work because all the anonymous functions you create in the loop refer to the same loop variable and therefore share its final value.

As a quick fix, you can replace the assignment with:

final = lambda x, f=f, final=final: f(final(x))

Or, you can return the lambda from a function:

def wrap(accum, f):
return lambda x: f(accum(x))
...
final = wrap(final, f)

To understand what's going on, try this experiment:

>>> l = [lambda: n for n in xrange(10)]
>>> [f() for f in l]
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]

This result surprises many people, who expect the result to be [0, 1, 2, ...]. However, all the lambdas point to the same n variable, and all refer to its final value, which is 9. In your case, all the versions of final which are supposed to nest end up referring to the same f and, even worse, to the same final.

The topic of lambdas and for loops in Python has been already covered on SO.

How to make function composition when i have list of functions in python?

The reduce function generally takes 2 arguments, a function (often a lambda), and a sequence. Sometimes you also want to provide a third argument, an initializer (initial value). You are providing more arguments.

Simple example of using reduce:

from functools import reduce

answer = reduce(lambda a, b: a+b, [0, 1, 2]) # a is the accumulated value and b the current value

print(answer) # prints 3

Here, lambda a, b: a+b is the function, and [0, 1, 2] is the sequence provided to the reduce function. Reduce will go over the sequence and execute the (lambda) function at every step. At every step the current value of the sequence and the accumulated value (from previous steps) are provided to the lambda function.

See the documentation on reduce

How to make a function composer

For your specific example, you can write

def round_sqrt(x):
return round(sqrt(x))

Alex's answer generalizes this; he defines a function that creates round_sqrt for you. If the function is already defined, you just pass it as an argument to rounder:

round_sqrt = rounder(sqrt)

Of course, you don't need to define round_sqrt if you don't want to. rounder(sqrt)(3.2) can be called directly, although it's far more efficient to safe the return value of rounder if you expect to use it multiple times, rather than redefining it each time.

Otherwise, the decorator syntax is just short for (using Alex's example)

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

adder = rounder(adder)

As I said in my comment, this is an example of implementing composition. Mathematically, composition is simple, because mathematical functions always take a single argument and return a single argument. As such, the composition of two functions f and g could always be defined simply as

def compose(f, g):
def h(x): # The name doesn't matter
return f(g(x))
return h

Then

round_sqrt = compose(round, sqrt)

(Ignoring all sorts of practical concerns around the implementation, Python could in theory even provide a Unicode operator for functions: round_sqrt = round ∘ sort. Explaining why this won't happen is beyond the scope of this answer.)

In Python, though, functions are far more complicated. They can take multiple arguments, they can accept arbitrary numbers of arguments and arbitrary keyword arguments, and while each technically returns a single value, that value can be a tuple which is thought of as multiple values or a dict. As a result, there may be many ways you might expect to pass the return value of g to a function f, more than can easily be accommodated in a simple compose function.

A function composition operator in Python

I am actually unwilling to provide this answer. But you should know in certain circumstance you can use a dot "." notation even it is a primary. This solution only works for functions that can be access from globals():

import functools

class Composable:

def __init__(self, func):
self.func = func
functools.update_wrapper(self, func)

def __getattr__(self, othername):
other = globals()[othername]
return lambda *args, **kw: self.func(other.func(*args, **kw))

def __call__(self, *args, **kw):
return self.func(*args, **kw)

To test:

@Composable
def add1(x):
return x + 1

@Composable
def add2(x):
return x + 2

print((add1.add2)(5))
# 8

How to compose a function n times in python

You use a loop, inside a nested function:

def compose(f, n):
def fn(x):
for _ in range(n):
x = f(x)
return x
return fn

fn will be have closure that retains references to the f and n that you called compose with.

Composite functions in python - dual compose

The suggested solution is needlessly complex. Countless reassignments of variables and a loop are a recipe for a headache. Here's a simplified alternative -

def dual (f, g, n):
if n == 0:
return lambda x: x
else:
return lambda x: f(dual(g, f, n - 1)(x))

add1 = lambda x: 1 + x
add2 = lambda x: 2 + x

print(dual(add1,add2,4)(3))
# 9
# (1 + 2 + 1 + 2 + 3)

print(dual(add1,add2,9)(3))
# 16
# (1 + 2 + 1 + 2 + 1 + 2 + 1 + 2 + 1 + 3)

print(dual(add1,add2,0)(3))
# 3

The reason this works is because in the recursive branch, we call dual with swapped arguments, dual(g,f,n-1). So f and g change places each time as n decrements down to 0, the base case, which returns the identity (no-op) function.

A slightly less readable version, but works identically -

def dual (f, g, n):
return lambda x: \
x if n == 0 else f(dual(g, f, n - 1)(x))

how to compose two or more functions in python

A few methods:

cipher_functions = lambda afile:swap_joker2(swap_joker1(afile))
def cipher_functions(afile):
return swap_joker2(swap_joker1(afile))

import functional #third party, not maintained. Alternatives exist
cipher_functions = functional.compose(swap_joker1, swap_joker2)

How can I use an operator to compose functions?

First only a certain amount of operator symbols are allowed in Python syntax. Dot "." is not a valid operator.

This page (the page is actually about the Python operator module, but the naming convention are the same to datamodel and the content is more organized) listed all available operators and the corresponding instance methods. For example, if you want to use "@" as the operator, you can write a decorator like this:

import functools

class Composable:

def __init__(self, func):
self.func = func
functools.update_wrapper(self, func)

def __matmul__(self, other):
return lambda *args, **kw: self.func(other.func(*args, **kw))

def __call__(self, *args, **kw):
return self.func(*args, **kw)

To test:

@Composable
def add1(x):
return x + 1

@Composable
def add2(x):
return x + 2

print((add1 @ add2)(5))
# 8


Related Topics



Leave a reply



Submit