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 asdef compose(f, g):
def h(x): # The name doesn't matter
return f(g(x))
return h
Thenround_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
Filename and Line Number of Python Script
Selecting Across Multiple Columns with Python Pandas
How to Pipe Input to Python Line by Line from Linux Program
How to Upsert Pandas Dataframe to Microsoft SQL Server Table
Appending to the Same List from Different Processes Using Multiprocessing
Detect 64Bit Os (Windows) in Python
Reading Binary Data from Stdin
How to Get Millisecond and Microsecond-Resolution Timestamps in Python
Append Dataframe to Excel with Pandas
Mapping a Range of Values to Another
Matplotlib Custom Marker/Symbol
Difference Between 'Python Setup.Py Install' and 'Pip Install'
What Are All the Dtypes That Pandas Recognizes
Python Using Variables from Another File
Brew Installation of Python 3.6.1: [Ssl: Certificate_Verify_Failed] Certificate Verify Failed