puzzled by name binding in local function
Ulrich Eckhardt
ulrich.eckhardt at dominolaser.com
Wed Feb 6 05:19:59 EST 2013
Dave and Terry,
Thanks you both for your explanations! I really appreciate the time you
took.
Am 05.02.2013 19:07, schrieb Dave Angel:
> If you need to have separate function objects that already know a
> value for i, you need to somehow bind the value into the function object.
>
> One way to do it, as you say, is with default parameters. A function's
> default parameters are each stored in the object, because they're
> defined to be evaluated only once. That's sometimes considered a flaw,
> such as when they're volatile, and subsequent calls to the function use
> the same value. But in your case, it's a feature, as it provides a
> standard place to store values as known at function definition time.
Yes, that was also the first way I found myself. The reason I consider
this non-obvious is that it creates a function with two parameters (one
with a default) while I only want one with a single parameter. This is
to some extent a bioware problem and/or a matter of taste, both for me
and for the other audience that I'm writing the code for.
> The other way to do it is with functions.partial(). I can't readily
> write you sample code, as I haven't messed with it in the case of class
> methods, but partial is generally a way to bind one or more values into
> the actual object. I also think it's clearer than the default parameter
> approach.
Partial would be clearer, since it explicitly binds the parameters:
import functools
class Foo(object):
def function(self, param):
print('function({}, {})'.format(self, param))
Foo.test = functools.partial(Foo.function, param=1)
f = Foo()
Foo.test(f) # works
f.test() # fails
I guess that Python sees "Foo.test" and since it is not a (nonstatic)
function, it doesn't create a bound method from this. Quoting the very
last sentence in the documentation: "Also, partial objects defined in
classes behave like static methods and do not transform into bound
methods during instance attribute look-up."
The plain-Python version mentioned in the functools documentation does
the job though, so I'll just use that with a fat comment. Also, after
some digging, I found http://bugs.python.org/issue4331, which describes
this issue. There is a comment from Jack Diederich from 2010-02-23 where
he says that using lambda or a function achieves the same, but I think
that this case shows that this is not the case.
I'm also thinking about throwing another aspect in there: Unless you're
using exec(), there is no way to put any variables as constants into the
function, i.e. to enforce early binding instead of the default late
binding. Using default parameters or functools.partial are both just
workarounds with limited applicability. Also, binding the parameters now
instead of later would reduce size and offer a speedup, so it could be a
worthwhile optimization.
> The main place where I see this type of problem is in a gui, where
> you're defining a callback to be used by a series of widgets, but you
> have a value that IS different for each item in the series. You write a
> loop much like you did, and discover that the last loop value is the
> only one used. The two cures above work, and you can also use lambda
> creatively.
Careful, lambda does not work, at least not easily! The problem is that
lambda only creates a local, anonymous function, but any names used
inside this function will only be evaluated when the function is called,
so I'm back at step 1, just with even less obvious code.
Greetings!
Uli
More information about the Python-list
mailing list