I wrote a code that gets n functions and composites them together. If the function doesnt recieve any input it returns the "x" which we called the lambda functions with.(example- compose()(3) # will return 3)
this is my code, i simply cant seem to see the problem:
def compose(*funcs):
if len(funcs)==0:
return lambda x: x
else:
for i in funcs[-1:0:-1]:
return lambda x: funcs[0](funcs[i](x))
Let's say you have three functions:
def f1(x):
return 'f1 %s' % x
def f2(x):
return 'f2 %s' % x
def f3(x):
return 'f3 %s' % x
Then we have a function compose:
def compose(*funcs):
def f(x):
ret = x
for func in funcs[::-1]:
ret = func(ret)
return ret
return f
We can use it like this:
F = compose(f1, f2, f3)
print F('x')
It will print out:
f1 f2 f3 x
Hope this is what you want.
The usual way to implement functional composition is this:
def compose(*funcs):
def _inner(x):
functools.reduce(lambda acc, f: f(acc), funcs, x)
return _inner
However, in order to fix your code, you might do this:
def compose(*funcs):
if not funcs: # preferred to checking if len is 0
return lambda x: x
else:
return lambda x: funcs[0](compose(*funcs[1:])(x))
i think you are talking about
def fn1(x):
return x+1
def fn2(x):
return x**2
def fn3(x):
return math.sin(x)**0.5
def apply(my_list_of_stuff):
if len(my_list_of_stuff) == 1:
return my_list_of_stuff[0]
return my_list_of_stuff[0](apply(my_list_of_stuff[1:]))
apply([fn1,fn2,fn3,7]) # -> fn1(fn2(fn3(7)))
assuming I understood what you are asking
Function composition can be elegantly achieved using reduce. You can use the initializer argument to achieve your no-input condition too:
import functools
def compose(*funcs):
return functools.reduce(lambda f, g: lambda x: f(g(x)), funcs, lambda x: x)
I think a better way to implement this is:
def compose(*funcs):
chain = lambda f, g: lambda *a, **kw: f(g(*a, **kw))
return reduce(chain, funcs, lambda x: x)
Which will return a new function that is the composition of the list of functions passed to it. If you don't care about kwargs, you can do:
def compose(*funcs):
chain = lambda f, g: lambda *a: f(g(*a))
return reduce(chain, funcs, lambda x: x)
Example:
>>> def compose(*funcs):
... chain = lambda f, g: lambda *a: f(g(*a))
... return reduce(chain, funcs, lambda x: x)
...
>>> remove_newlines = compose(lambda l: "".join(l), lambda s: s.split("\n"))
>>> remove_newlines("hello\n world")
'hello world'
Note: In python3, reduce has been moved out of the stdlib into functools, so you will need to do from functools import reduce
Related
Say that I have a list of functions: [f1, f2, f3] (in python)
How do I return a single function F:=f3(f2(f1())) (notice that F is of function type). I know that it's possible to do it with .reduce() but I was wondering if there's another way to do it without using libraries.
edit:
Also, notice that the length of the list can be greater than 3
I tried:
def func(list):
i = 1
new_function = filters[0]
while i<=len(filters)-1:
new_function = filters[i](new_function)
i+=1
return new_function
but it doesn't work
The problem in your code is that you pass a function as argument with filters[i](new_function).
I would suggest this recursive solution:
def chain(first, *rest):
return lambda x: first(chain(*rest)(x) if rest else x)
Example use:
def inc(x):
return x + 1
def double(x):
return x * 2
def square(x):
return x * x
f = chain(square, double, inc)
print(f(5)) # ((5+1)*2) ** 2 == 144
I see that in the code you tried, you never actually call the first of your functions. (I also assume that your code starts: def func(filters):
Taking into account that f1() takes no parameter, but the rest take the parameter of the return of the previous function, this should work:
def fit(funcs):
v = funcs[0]()
for f in funcs[1:]:
v = f(v)
return v
def f1():
return 42
def f2(x):
return x
def f3(x):
return x
fs = [f1, f2, f3]
a = lambda:fit(fs)
print(a())
Output: 42
def get_single_func(func_list):
def single_func(*args, **kwargs):
ret = func_list[0](*args, **kwargs)
for func in func_list[1:]:
ret = func(ret)
return ret
return single_func
I have a helping function:
def incr(x):
return x+1
I want to create a function named "repeated" that use "incr" function n times on a certain parameter
In the end I want to use the "repeated" function in this matter only :
repeated (incr, 4)(2)
That for example will output 6.
So far I tried to do this:
def repeated(f, n):
func, x = f
for i in range(n):
func(x)
But it gave me an error saying I can't unpack a non Tuple function.
It doesn't seem like I don't have access in the function to the "(2)"
I do not recommend to use such a syntax construct, for such a task:
repeated(incr, 4)(2)
Your repeated function must return another function, that will be called by (2).
This should work in your requested manner:
def incr(x):
return x+1
def repeated(f, x):
# function foo will be returned by repeated and called by (2)
def foo(n):
res = x
for i in range(n):
res = f(res)
return res
return foo
print(repeated(incr, 4)(2))
I think you may want to do something like functional programming.
Add args to deal with for different kind of function you want to repeat.
I can't confirm if there is a position argument what kind of results you want, so I didn't deal with it.
code:
import functools
def incr(x):
return x + 1
def incx(x,y = 0):
return x + y + 1
def repeated_inner(func,args,times):
head, *rest = args
for _ in range(times):
head = func(head, *rest)
return args[0]
def repeated(func, *args ):
return functools.partial(repeated_inner, func, args)
print(repeated(incr, 4)(2))
print(repeated(incx, 4)(2))
print(repeated(incx, 4 ,3)(2))
result
6
6
12
the repeatedfunction must return a function
def repeated(func, n):
def repeatedfunc(x):
rsl = x
for i in range(n):
rsl = func(rsl)
return rsl
return repeatedfunc
def incr(x):
return x+1
rslt = repeated(incr, 4)(2)
print(rslt)
output
6
You should write something like this:
def repeated(f, arg_0, n):
arg = arg_0
for i in range(n):
arg = f(arg)
return arg
In a more general situation:
def repeated(f, arg):
def n_f(n):
result = 0
for i in range(n):
result =f(arg)
return result
return n_f
I have a long list of functions that all have the same initial part.
default_value = 123
def func_1(input):
if apply_some_check(input):
return default_value
return do_something_1(input)
def func_2(input):
if apply_some_check(input):
return default_value
return do_something_2(input)
def func_3(input):
if apply_some_check(input):
return default_value
return do_something_3(input)
......
Is there any way to reuse the
if apply_some_check(input):
return default_value
part of the code, while keeping this list of functions (required by my code reviewer for readability)?
You can use decorators for this.
def checked(f):
def inner(inp):
if apply_some_check(inp):
return default_value
return f(inp)
return inner
# checked takes a function and returns a function, we can now use it as a function decorator
#checked
def some_func(inp):
return do_something(inp)
You could also try to use partial if it fits to your needs as follows:
from functools import partial
default_value = 123
def apply_some_check(a):
return False
def func_template(do_something, a):
if apply_some_check(a):
return default_value
return do_something(a)
def do_something_1(a):
return "test1" == a
def do_something_2(a):
return "test2" == a
def do_something_3(a):
return "test3" == a
if __name__ == '__main__':
a = "test2"
funcs = []
for i in range(1, 4):
funcs.append(partial(func_template, eval(f"do_something_{i}"), a))
for f in funcs:
print(f())
Result:
False
True
False
I have a list which I want to sort by multiple keys, like:
L = [ ... ]
L.sort(key = lambda x: ( f(x), g(x) ))
This works fine. However, this results with unnecessary calls to g, which I would like to avoid (for being potentially slow). In other words, I want to partially and lazily evaluate the key.
For example, if f is unique over L (i.e. len(L) == len(set(map(f,L)))) no calls to g should be made.
What would be the most elegant/pythonic way to do this?
One way I can think of is to define a custom cmp function (L.sort(cmp=partial_cmp)), but IMO this is less elegant and more complicated than using the key parameter.
Another way would be to define a key-wrapper class which takes a generator expression to generate the different parts of the key, and override the comparison operators to compare one-by-one. However, I'm feeling there must be a simpler way...
EDIT: I'm interested in a solution for the general problem of sorting by multiple functions, not only two as in my example above.
You can try using itertools.groupby:
result = []
for groupKey, group in groupby(sorted(L, key=f), key=f):
sublist = [y for y in group]
if len(sublist) > 1:
result += sorted(sublist, key=g)
else:
result += sublist
Another possibility, even less elegant, but in place:
L.sort(key = f)
start = None
end = None
for i,x in enumerate(L):
if start == None:
start = i
elif f(x) == f(L[start]):
end = i
elif end == None:
start = i
else:
L[start:end+1] = sorted(L[start:end+1], key=g)
start = None
if start != None and end != None:
L[start:end+1] = sorted(L[start:end+1], key=g)
First version generalized to any number of functions:
def sortBy(l, keyChain):
if not keyChain:
return l
result = []
f = keyChain[0]
for groupKey, group in groupby(sorted(l, key=f), key=f):
sublist = [y for y in group]
if len(sublist) > 1:
result += sortBy(sublist, keyChain[1:])
else:
result += sublist
return result
The second version generalized to any number of functions (not fully in place though):
def subSort(l, start, end, keyChain):
part = l[start:end+1]
sortBy(part, keyChain[1:])
l[start:end+1] = part
def sortBy(l, keyChain):
if not keyChain:
return
f = keyChain[0]
l.sort(key = f)
start = None
end = None
for i,x in enumerate(l):
if start == None:
start = i
elif f(x) == f(l[start]):
end = i
elif end == None:
start = i
else:
subSort(l, start, end, keyChain)
start = i
end = None
if start != None and end != None:
subSort(l, start, end, keyChain)
Given a function, you could create a LazyComparer class like this:
def lazy_func(func):
class LazyComparer(object):
def __init__(self, x):
self.x = x
def __lt__(self, other):
return func(self.x) < func(other.x)
def __eq__(self, other):
return func(self.x) == func(other.x)
return lambda x: LazyComparer(x)
To make a lazy key function out of multiple functions, you could create a utility function:
def make_lazy(*funcs):
def wrapper(x):
return [lazy_func(f)(x) for f in funcs]
return wrapper
And together they could be used like this:
def countcalls(f):
"Decorator that makes the function count calls to it."
def _f(*args, **kwargs):
_f._count += 1
return f(*args, **kwargs)
_f._count = 0
return _f
#countcalls
def g(x): return x
#countcalls
def f1(x): return 0
#countcalls
def f2(x): return x
def report_calls(*funcs):
print(' | '.join(['{} calls to {}'.format(f._count, f.func_name)
for f in funcs]))
L = range(10)[::-1]
L.sort(key=make_lazy(f1, g))
report_calls(f1, g)
g._count = 0
L.sort(key=make_lazy(f2, g))
report_calls(f2, g)
which yields
18 calls to f1 | 36 calls to g
36 calls to f2 | 0 calls to g
The #countcalls decorator above is used to connfirm that when f1 returns a lot
of ties, g is called to break the ties, but when f2 returns distinct values,
g does not get called.
NPE's solution adds memoization within the Key class. With the solution above,
you could add memoization outside (independent of) the LazyComparer class:
def memo(f):
# Author: Peter Norvig
"""Decorator that caches the return value for each call to f(args).
Then when called again with same args, we can just look it up."""
cache = {}
def _f(*args):
try:
return cache[args]
except KeyError:
cache[args] = result = f(*args)
return result
except TypeError:
# some element of args can't be a dict key
return f(*args)
_f.cache = cache
return _f
L.sort(key=make_lazy(memo(f1), memo(g)))
report_calls(f1, g)
which results in fewer calls to g:
10 calls to f1 | 10 calls to g
You could use a key object that would lazily evaluate and cache g(x):
class Key(object):
def __init__(self, obj):
self.obj = obj
self.f = f(obj)
#property
def g(self):
if not hasattr(self, "_g"):
self._g = g(self.obj)
return self._g
def __cmp__(self, rhs):
return cmp(self.f, rhs.f) or cmp(self.g, rhs.g)
Here is an example of use:
def f(x):
f.count += 1
return x // 2
f.count = 0
def g(x):
g.count += 1
return x
g.count = 0
L = [1, 10, 2, 33, 45, 90, 3, 6, 1000, 1]
print sorted(L, key=Key)
print f.count, g.count
i want to make a decoration method to assign the variable which the function would use but wouldn't be deliver by itself.
for example add new variable y in lambda r,i wrote code in this way but didn't work.
r = lambda x:x+y
def foo(func):
def wrapped(*args,**kwargs):
y = 3
return func(y=y,*args,**kwargs)
return wrapped
r = foo(r)
print(r(444))
this wouldn't work too
r = lambda x:x+y
def foo(func):
def wrapped(*args,**kwargs):
y = 3
return func(*args,**kwargs)
return wrapped
r = foo(r)
print(r(444))
kwargs is a casual python dict type, so you can just set the value of the y key to be 3
r = lambda x, y=0:x+y
def foo(func):
def wrapped(*args,**kwargs):
print type(kwargs) # will output <type 'dict'>
kwargs['y'] = 3
return func(*args,**kwargs)
return wrapped
In Understanding kwargs in Python this is explained in details.
Problem is that the function r can accept only one argument, you need to change its definition to accept more args:
r = lambda x, y=0, *args, **kwargs: x + y
def foo(func):
def wrapped(*args,**kwargs):
y = 3
return func(y=y, *args,**kwargs)
return wrapped
r = foo(r)
print(r(444))
#447