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