Search code examples
pythonembeddedclosuresipythonpartial-application

Dynamic generation of partial functions in Python 2.7.x


Say I want to dynamically create a function on an IPython shell from the following lambda:

f = lambda x, ci: np.percentile(x, 100-ci)

that fixes ci to a new value. It would be something like the following (create_new_f is what I am looking for).

ci = 20
new_f = create_new_f(f, ci=ci)
result = new_f([20,30,50,80])

I have tried using functools.partial as:

new_f = functools.partial(f, ci=20)

but when I run this in an embedded shell in IPython I get:

AttributeError: 'functools.partial' object has no attribute '__module__'

is there any alternative way of doing this, perhaps using decorators?

Background on the topic:

  • Here is an IPython issue that prompted the question.
  • Here is a thread explaining the problem (and fix in Python 3.x only) as well.
  • Here is another thread in SO that shows the problem (see the top comment)
  • For an even deeper analysis of this problem, see this issue in IPython.

again, what I am looking for is an alternative way of doing this.


Solution

  • Solution for embedded IPython,

    You can implement with a closure (I believe this may be your solution):

    import numpy as np
    def partial_f(ci):
        def fn(x):
            return np.percentile(x, 100-ci)
        return fn
    new_f = partial_f(20)
    new_f([20,30,50,80])
    

    returns

    62.000000000000014
    

    To Check:

    functools.partial works for me in my IDE's shell at work:

    import functools
    import numpy as np
    f = lambda x, ci: np.percentile(x, 100-ci)
    ci = 20
    new_f = functools.partial(f, ci=ci)
    new_f([20,30,50,80])
    

    returns

    62.000000000000014
    

    With lambdas (this does work in the IPython online shell I tried it out on, but apparently doesn't work for your case):

    import numpy as np
    pf = lambda ci: lambda x: np.percentile(x, 100-ci)
    new_f2 = pf(20)
    new_f2([20,30,50,80])
    

    also returns

    62.000000000000014