Closure/binding problem or misunderstanding

B

Bob.Sidebotham

If I run the following code:

class path(object):
def __init__(self, **subdirs):
for name, path in subdirs.iteritems():
def getpath():
return path
setattr(self, name, getpath)

export = path(
one = 'this is one',
two = 'this is two',
)

print "invoking", export.one, export.one()
print "invoking", export.two, export.two()

I get this output:

invoking <function getpath at 0x400ded14> this is one
invoking <function getpath at 0x400ded84> this is one

So there apparently are two definitions of the function "getpath" (the
addresses are different, anyway), but they seem to have the same value
for the binding of "path". It's not clear to me, after reading what I
can find about python name binding, whether this is the expected
behavior, or not (although I was surprised).

Can anyone enlighten me?

Thanks,
Bob Sidebotham
 
P

Paul Hankin

If I run the following code:

class path(object):
def __init__(self, **subdirs):
for name, path in subdirs.iteritems():
def getpath():
return path
setattr(self, name, getpath)

export = path(
one = 'this is one',
two = 'this is two',
)

print "invoking", export.one, export.one()
print "invoking", export.two, export.two()

I get this output:

invoking <function getpath at 0x400ded14> this is one
invoking <function getpath at 0x400ded84> this is one

So there apparently are two definitions of the function "getpath" (the
addresses are different, anyway), but they seem to have the same value
for the binding of "path". It's not clear to me, after reading what I
can find about python name binding, whether this is the expected
behavior, or not (although I was surprised).

Clearly it wasn't the expected behaviour, or you wouldn't be
surprised :)

It's behaving as defined though, and the usual work-around is to add a
variable with a default value.

class path(object):
def __init__(self, **subdirs):
for name, path in subdirs.iteritems():
def getpath(path=path):
return path
setattr(self, name, getpath)
 
B

Boris Borcic

If I run the following code:

class path(object):
def __init__(self, **subdirs):
for name, path in subdirs.iteritems():
def getpath():
return path
setattr(self, name, getpath)

export = path(
one = 'this is one',
two = 'this is two',
)

print "invoking", export.one, export.one()
print "invoking", export.two, export.two()

I get this output:

invoking <function getpath at 0x400ded14> this is one
invoking <function getpath at 0x400ded84> this is one

So there apparently are two definitions of the function "getpath" (the
addresses are different, anyway), but they seem to have the same value
for the binding of "path". It's not clear to me, after reading what I
can find about python name binding, whether this is the expected
behavior, or not (although I was surprised).

Can anyone enlighten me?

Yes this is the expected behavior. Both your getpath() functions return the
current value of the single path variable at the time of invocation. Perhaps
this would be the slightest bit clearer if subdir.iteritems() did not provide
your items in swapped order (so that the returned values would be "this is two").

To create distinct bindings to distinct values of path at the distinct
executions of "def getpath() :...", you should write
"def getpath(path=path) :..."

HTH, BB
 
B

Bob.Sidebotham

It's behaving as defined though, and the usual work-around is to add a
variable with a default value.

class path(object):
def __init__(self, **subdirs):
for name, path in subdirs.iteritems():
def getpath(path=path):
return path
setattr(self, name, getpath)

Thanks, Paul. That's helpful. I will re-read the reference manual, and
see if I can find out where this behavior is defined. It looks like
it's binding both locals and globals, but not actually taking a
snapshot in time, as would, say Perl (I think). From an efficiency POV
this makes great sense, and I can see that you get everything you need
by essentially creating the closure yourself (by putting everything in
the local space for the function).

Bob
 
B

Bob.Sidebotham

Yes this is the expected behavior. Both your getpath() functions return the
current value of the single path variable at the time of invocation. Perhaps
this would be the slightest bit clearer if subdir.iteritems() did not provide
your items in swapped order (so that the returned values would be "this is two").

Yes, this helps, too. It's actually quite elegantly simple (it's just
not fully obvious (from the manual) until you completely grok the way
namespaces work).

Thanks,
Bob
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top