how to write function that returns function

Bengt Richter bokr at oz.net
Tue May 14 21:37:23 EDT 2002


On 14 May 2002 16:03:25 -0700, spam at bugbear.com (Paul Graham) wrote:

>I am not a Python expert, and I'm hoping someone
>can tell me how in Python to write a function
>of one argument x that returns a function of one
>argument y that returns x+y.
>
>Here, in Scheme, is what I want to write:
>
>(define foo (x) (lambda (y) (+ x y)))
>
With nested scopes (default in 2.2, import nested_scopes from __future__ in 2.1),
you can do this:

 >>> def foo(x): return lambda y: x+y
 ...
 >>> f = foo(7)
 >>> f(3)
 10
 >>> import dis                             <-- handy disassembler for seeing what functions do etc.
 >>> dis.dis(f)
           0 SET_LINENO               1
           3 LOAD_DEREF               0 (x) <-- this loads the 7 from the closure
           6 LOAD_FAST                0 (y) <-- this loads the argument to the function
           9 BINARY_ADD
          10 RETURN_VALUE
 >>> f.func_closure
 (<cell at 0x008430C0: int object at 0x00795910>,)
 >>> hex(id(7))
 '0x795910'

Doing it as above captures x in a closure. I used a
small int 7 knowing the same instance would be used,
and we could recognize it in the closure by its id.

>I found on the web a page that says I could define 
>this as follows:
>
>def addn(x):
>  return lambda y,z=y: x+z
>
I don't think that's quite right. The idea is to capture x in
a way that works like the closure, and a dummy default value is
the mechanism, so I think it should be:

 >>> def foo(x): return lambda y,z=x: y+z
 ...
 >>> f = foo(7)
 >>> f(3)
 10
 >>> dis.dis(f)
           0 SET_LINENO               1
           3 LOAD_FAST                0 (y) <-- this loads the first function arg
           6 LOAD_FAST                1 (z) <-- this simulates the closure effect
           9 BINARY_ADD                         \_ unless you pass a second arg
          10 RETURN_VALUE
 >>> f.func_closure          <-- there isn't any (None)
 >>> f.func_defaults
 (7,)
 >>> f(3,5)       <-- the second arg overrides, so it's not a proper solution
 8

>but I don't think this is exactly the same thing,
>because it returns a function that takes a second
>optional argument.  That is a substantial difference.
>If the Scheme function is inadvertently called 
>(e.g. in someone else's code) with two arguments, it 
>would signal an error, whereas the code above would 
>quietly give the wrong answer.
>
right

>I would appreciate it if someone could tell me the
>standard way to write this so that it returns a
>function of exactly one argument.
>
See above, but BTW, you don't have to use lambda at all:

 >>> def foo(x):
 ...     def forget_this_name(y):
 ...         return x+y
 ...     return forget_this_name
 ...
 >>> f = foo(7)
 >>> f(3)
 10
 >>> dis.dis(f)
           0 SET_LINENO               2

           3 SET_LINENO               3
           6 LOAD_DEREF               0 (x)  <-- this loads the 7 from the closure
           9 LOAD_FAST                0 (y)  <-- this loads the function arg
          12 BINARY_ADD
          13 RETURN_VALUE
          14 LOAD_CONST               0 (None) <--this is dead boilerplate code you don't get with lambda
          17 RETURN_VALUE
 >>> f.func_closure
 (<cell at 0x008430C0: int object at 0x00795910>,)  <-- proper closure this time. Note hex location
 >>> f.func_defaults                                <-- no defaults
 >>> f(3,5)                                         <-- strictly one arg, so this fails
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: forget_this_name() takes exactly 1 argument (2 given)
 >>> hex(id(7))
 '0x795910'                                         <--hex location of 7

HTH

Regards,
Bengt Richter



More information about the Python-list mailing list