Pythonic recipe to get more flexible function currying

Viewed 55

Builtin python offers functools.partial for currying, but alone it's quite limited. My guess is that there's other builtins around whose combination with partial would overcome these limitations. What are they?

A few examples.

I can do this:

asdf_is_instance_of = partial(isinstance, 'asdf')
assert asdf_is_instance_of(str)

but I can't do this (arguably more frequently useful thing to have):

isinstance_of_str = partial(isinstance, class_or_tuple=str)
isinstance_of_str('asdf')

because isinstance() takes no keyword arguments.

This is one type of limitation: Having no way to circumvent parameter kind limitations directly with partial. For this case one could do:

def my_isinstance(obj, class_or_tuple):  # just to get rid of position only parameter kinds
    return isinstance(obj, class_or_tuple)

isinstance_of_str = partial(my_isinstance, class_or_tuple=str)
assert isinstance_of_str('asdf')  # now it works

There's another frequent limitation: Parameter order. One could write a partial_ext that would take take care of both parameter kind and order. This currying function would be such that

def foo(x,y,z):
    return x + y * z

xz_foo_with_y_equal_228 = partial_ext(foo, y=228)

be equivalent to

def xz_foo_with_y_equal_228(x, y, z):
    return foo(x, y=228, z)

There seems to be no such currying builtin in python, so my guess is there's some kind recipe involving other builtins that takes care of it.

Anyone know that secret?

1 Answers

Python is not a functional programming language; it is a multi-paradigm language which has some support for some limited functional programming techniques. If you want to do partial application with the second positional argument of a function, I doubt there is anything in the standard library which supports that "out of the box". You could write your own helper function like this:

def partial_second(f, y):
    def partial_f(x, *vargs, **kwargs):
        return f(x, y, *vargs, **kwargs)
    return partial_f

is_string = partial_second(isinstance, str)

But I would only suggest doing this if you frequently have to do this kind of currying, and in that case you probably aren't writing idiomatic Python anyway. According to the Zen of Python, "explicit is better than implicit" and "simple is better than complex", so instead of currying, you could just write like this instead of using partial application:

def is_string(x):
    return isinstance(x, str)
Related