[Tutor] Passing functions as arguments to other functions
Steven D'Aprano
steve at pearwood.info
Fri Sep 30 07:55:57 EDT 2016
On Thu, Sep 29, 2016 at 09:43:57PM -0500, boB Stepp wrote:
> I believe I understand the barebone mechanics on how to do this. But
> I do not understand the rationale of why Python does it the way it
> does. Say
>
> def f(g, *args):
> g(*args)
Right. This says:
- Define a function called "f", which takes one named parameter, "g",
and collect any other arguments under the name "args".
- For the body of "f", simply call that argument "g" with all the other
arguments.
You probably want `return g(*args)` rather than just `g(*args)`.
> def g(*args):
> # Do something.
>
> do_things = f(g, *args)
As it stands now, f() is a middle-man that adds nothing to the picture.
In practice, to be worth calling f() rather than just calling g()
directly, you would want it to do some sort of additional processing:
either manipulate the arguments, or the result returned by g().
> is the outline of how I understand the mechanics of doing this. But
> noob boB initially want to do instead:
>
> def f(g(*args)):
> g(*args)
[...]
> which, of course, will give me a syntax error.
Of course. I'm not sure what you think a parameter called "g(*args)"
even means.
When you write this:
def f(x):
return x+1
do you feel the need to write:
def f(x+1):
return x+1
instead? I expect not. Can you see how the two situations are
equivalent? My example uses +1, your example uses "call the function
with these arguments" instead, but the two are otherwise equivalent.
In common English, there is a simple name for the +1 part of x+1,
namely "plus one" or "add one" or similar. But I don't know of any
simple name for the `(*args)` part of `g(*args)`. "Call g with given
arguments" perhaps, but that's not really simple. The situations are
conceptually the same:
+ is a binary (two argument) operator that takes the object on the left,
x, and the object on the right, 1, and adds them together;
() is an n-ary (n arguments) operator (or perhaps "pseudo-operator" if
you prefer) that takes the object on the left, g, and the multiple
arguments inside the brackets, *args, and applies the arguments to
function g.
> Also, I note that if I just type a function name without the
> parentheses in the interpreter, I will get something like this:
>
> >>> def f():
> pass
>
> >>> f
> <function f at 0x000001F775A97B70>
>
> So the impression I am getting is that a function name by itself (with
> no parentheses) is the function *object*.
Correct.
> But why does Python require
> separating the function object from its parameters when it is being
> passed as an argument to another function?
If you pass the function arguments to the function *first*, then it gets
evaluated before the second function gets to see it. Let's investigate:
def g(*args):
print("Calling function g with arguments {}".format(args))
return 999
def f(func, *args):
print("f received first argument: {}".format(func))
print("f received additional arguments: {}".format(args))
return func(*args)
Let's try it the right way:
py> f(g, 1, 2, 3)
f received first argument: <function g at 0xb7ac965c>
f received additional arguments: (1, 2, 3)
Calling function g with arguments (1, 2, 3)
999
And the wrong way:
py> f(g(1, 2, 3))
Calling function g with arguments (1, 2, 3)
f received first argument: 999
f received additional arguments: ()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in f
TypeError: 'int' object is not callable
Does this help?
--
Steve
More information about the Tutor
mailing list