473,757 Members | 10,007 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

closures and dynamic binding

Hello all,

To me, this is a somewhat unintuitive behavior. I want to discuss the
parts of it I don't understand.
>>f= [ None ]* 10
for n in range( 10 ):
.... f[ n ]= lambda: n
....
>>f[0]()
9
>>f[1]()
9

I guess I can accept this part so far, though it took a little getting
used to. I'm writing some code and found the following workaround,
but I don't think it should give different results. Maybe I'm not
understanding some of the details of closures.
>>f= [ None ]* 10
for n in range( 10 ):
.... f[ n ]= (lambda n: ( lambda: n ) )( n )
....
>>f[0]()
0
>>f[1]()
1

Which is of course the desired effect. Why doesn't the second one
just look up what 'n' is when I call f[0], and return 9?
Sep 28 '08 #1
26 2810
On Sat, 27 Sep 2008 21:43:15 -0700, Aaron \"Castironpi \" Brady wrote:
To me, this is a somewhat unintuitive behavior. I want to discuss the
parts of it I don't understand.
>>>f= [ None ]* 10
for n in range( 10 ):
... f[ n ]= lambda: n
...
>>>f[0]()
9
>>>f[1]()
9
`n` is looked up at the time ``f[0]`` is called. At that time it is
bound to 9.
>>>f= [ None ]* 10
for n in range( 10 ):
... f[ n ]= (lambda n: ( lambda: n ) )( n ) ...
>>>f[0]()
0
>>>f[1]()
1

Which is of course the desired effect. Why doesn't the second one just
look up what 'n' is when I call f[0], and return 9?
It *does* look up `n` at the time you call ``f[0]`` but this time it's
the local `n` of the outer ``lambda`` function and that is bound to
whatever the function was called with. At the time it was called the
global `n` was bound to 0. Maybe it get's more clear if you don't name
it `n`:

In [167]: f = [None] * 10

In [168]: for n in xrange(10):
.....: f[n] = (lambda x: lambda: x)(n)
.....:

In [169]: f[0]()
Out[169]: 0

Ciao,
Marc 'BlackJack' Rintsch
Sep 28 '08 #2
On Sep 28, 1:14*am, Marc 'BlackJack' Rintsch <bj_...@gmx.net wrote:
On Sat, 27 Sep 2008 21:43:15 -0700, Aaron \"Castironpi \" Brady wrote:
To me, this is a somewhat unintuitive behavior. *I want to discuss the
parts of it I don't understand.
>>f= [ None ]* 10
for n in range( 10 ):
... * * f[ n ]= lambda: n
...
>>f[0]()
9
>>f[1]()
9

`n` is looked up at the time ``f[0]`` is called. *At that time it is
bound to 9.
>>f= [ None ]* 10
for n in range( 10 ):
... * * f[ n ]= (lambda n: ( lambda: n ) )( n ) ...
>>f[0]()
0
>>f[1]()
1
Which is of course the desired effect. *Why doesn't the second one just
look up what 'n' is when I call f[0], and return 9?

It *does* look up `n` at the time you call ``f[0]`` but this time it's
the local `n` of the outer ``lambda`` function and that is bound to
whatever the function was called with. *At the time it was called the
global `n` was bound to 0. *Maybe it get's more clear if you don't name
it `n`:

In [167]: f = [None] * 10

In [168]: for n in xrange(10):
* *.....: * * f[n] = (lambda x: lambda: x)(n)
* *.....:

In [169]: f[0]()
Out[169]: 0

Ciao,
* * * * Marc 'BlackJack' Rintsch
Hi Marc,

It's my understanding that 'n' gets a new value every time through the
loop. n= 0 on the first pass, n= 1 on the second pass, and so on. n=
9 by the end, and that's why `lambda: n` always returns 9. It queries
the variable 'n', and finds 9. (This got lengthy. I started thinking
aloud.)

In your version of the indirect example, it queries the variable 'x',
which it finds in a new distinct scope in each f element. In f[0], x=
0. In f[1], x= 1. There are 10 different 'x' variables throughout
the contents of f. The direct example does not do this allocation of
ten different 'x's.

It's sort of helping. I think I feel like the following is more like
what I'm looking for:

(Non-standard)
>>f= [ None ]* 10
for n in range( 10 ):
.... f[ n ]= n
....
>>f[0]
9
>>f[1]
9

because when you access f[0], it looks up the variable 'n'. Obviously
not.

(Unproduced)
>>f= [ None ]* 10
for n in range( 10 ):
.... f[ n ]= late( n ) #late/lazy
....
>>f[0]
9
>>f[1]
9
>>f= [ None ]* 10
for n in range( 10 ):
.... f[ n ]= early( n ) #early/eager
....
>>f[0]
0
>>f[1]
1

For the functions, I want a function that returns 'n', either late or
early.

(Unproduced)
>>for n in range( 10 ):
.... f[ n ]= lambda: late( n )
>>f[0]()
9
>>for n in range( 10 ):
.... f[ n ]= lambda: early( n )
>>f[0]()
0

I don't think you could pull this off. 'late' and 'early' succeed
with quotes around n, early('n') and late('n'), in the direct
assignments, but the functions aren't so lucky. 'n' has gone on to a
better world by the time 'early' gets any control.

This might have some success.

(Unproduced)
>>for n in range( 10 ):
.... f[ n ]= late( lambda: n )
>>f[0]()
9
>>for n in range( 10 ):
.... f[ n ]= early( lambda: n )
>>f[0]()
0

Though it's beyond my foresight to tell if it's feasible as stated, if
you need quotes, how much introspection you would need, etc. Plus,
'late' and 'early' were accepting quoted parameters earlier. How
would they look inside a function? Could a simple decorator provide
the service?

On a tangent about mutables, it's not that numbers, strings, and
tuples are 'immutable' per se, it's just that they don't have any
methods which mutate them (or assignable properties). Lists and
dictionaries do. It's up to the user whether a custom class does.
Sep 28 '08 #3
Aaron "Castironpi " Brady wrote:
Hello all,

To me, this is a somewhat unintuitive behavior. I want to discuss the
parts of it I don't understand.
>>>f= [ None ]* 10
for n in range( 10 ):
... f[ n ]= lambda: n
This is equivalent to

for n in range(10):
def g(): return n
f[n] = g

which is equivalent to

def g(): return n
f = [g]*10
n = 9
>>>f[0]()
9
>>>f[1]()
9
which make this not so surprising as the original lambda version is to
some people.
I guess I can accept this part so far, though it took a little getting
used to. I'm writing some code and found the following workaround,
but I don't think it should give different results. Maybe I'm not
understanding some of the details of closures.
>>>f= [ None ]* 10
for n in range( 10 ):
... f[ n ]= (lambda n: ( lambda: n ) )( n )
This is equivalent to

for n in range(10):
def g(n):
def h:
return n
return h
f[n] = g(n)

Now, to avoid the needless confusion of 'n's, g is equivalent to

def g(x):
def h:
return x
return h

(One could do the same change in the lambdas, too, of course).
so that g(n)() == n, with n stored in each closure h...
...
>>>f[0]()
0
>>>f[1]()
1
to be regurgitated when each is called.

Terry Jan Reedy

Sep 28 '08 #4
On Sat, 27 Sep 2008 21:43:15 -0700, Aaron \"Castironpi \" Brady wrote:
Hello all,

To me, this is a somewhat unintuitive behavior. I want to discuss the
parts of it I don't understand.
>>>f= [ None ]* 10
for n in range( 10 ):
... f[ n ]= lambda: n
...
>>>f[0]()
9
>>>f[1]()
9

I guess I can accept this part so far, though it took a little getting
used to. I'm writing some code and found the following workaround, but
I don't think it should give different results. Maybe I'm not
understanding some of the details of closures.
>>>f= [ None ]* 10
for n in range( 10 ):
... f[ n ]= (lambda n: ( lambda: n ) )( n )
...
>>>f[0]()
0
>>>f[1]()
1

Which is of course the desired effect. Why doesn't the second one just
look up what 'n' is when I call f[0], and return 9?
That's an awfully complicated solution. A much easier way to get the
result you are after is to give each function its own local copy of n:

f[n] = lambda n=n: n

As for why the complicated version works, it may be clearer if you expand
it from a one-liner:

# expand: f[ n ]= (lambda n: ( lambda: n ) )( n )

inner = lambda: n
outer = lambda n: inner
f[n] = outer(n)

outer(0) =inner with a local scope of n=0
outer(1) =inner with a local scope of n=1 etc.

Then, later, when you call inner() it grabs the local scope and returns
the number you expected.
--
Steven
Sep 28 '08 #5
On Sep 28, 2:52*am, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com .auwrote:
On Sat, 27 Sep 2008 21:43:15 -0700, Aaron \"Castironpi \" Brady wrote:
Hello all,
To me, this is a somewhat unintuitive behavior. *I want to discuss the
parts of it I don't understand.
>>f= [ None ]* 10
for n in range( 10 ):
... * * f[ n ]= lambda: n
...
>>f[0]()
9
>>f[1]()
9
I guess I can accept this part so far, though it took a little getting
used to. *I'm writing some code and found the following workaround, but
I don't think it should give different results. *Maybe I'm not
understanding some of the details of closures.
>>f= [ None ]* 10
for n in range( 10 ):
... * * f[ n ]= (lambda n: ( lambda: n ) )( n )
...
>>f[0]()
0
>>f[1]()
1
Which is of course the desired effect. *Why doesn't the second one just
look up what 'n' is when I call f[0], and return 9?

That's an awfully complicated solution. A much easier way to get the
result you are after is to give each function its own local copy of n:

f[n] = lambda n=n: n

As for why the complicated version works, it may be clearer if you expand
it from a one-liner:

# expand: f[ n ]= (lambda n: ( lambda: n ) )( n )

inner = lambda: n
outer = lambda n: inner
f[n] = outer(n)

outer(0) =inner with a local scope of n=0
outer(1) =inner with a local scope of n=1 etc.

Then, later, when you call inner() it grabs the local scope and returns
the number you expected.

--
Steven
Steven,

I must have misunderstood. Here's my run of your code:
>>inner = lambda: n
outer = lambda n: inner
outer(0)
<function <lambdaat 0x00A01170>
>>a=outer(0)
b=outer(1)
a()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
NameError: global name 'n' is not defined

Why doesn't 'inner' know it's been used in two different scopes, and
look up 'n' based on the one it's in?
Sep 28 '08 #6
Aaron "Castironpi " Brady wrote:
On Sep 28, 2:52 am, Steven D'Aprano <st...@REMOVE-THIS-
>As for why the complicated version works, it may be clearer if you expand
it from a one-liner:

# expand: f[ n ]= (lambda n: ( lambda: n ) )( n )

inner = lambda: n
outer = lambda n: inner
f[n] = outer(n)

outer(0) =inner with a local scope of n=0
outer(1) =inner with a local scope of n=1 etc.
For this to work, the 'expansion' has to be mental and not actual.
Which is to say, inner must be a text macro to be substituted back into
outer.
>Then, later, when you call inner() it grabs the local scope and returns
the number you expected.
>
I must have misunderstood. Here's my run of your code:
I cannot speak to what Steven meant, but
>>>inner = lambda: n
when inner is actually compiled outside of outer, it is no longer a
closure over outer's 'n' and 'n' will be looked for in globals instead.
>>>outer = lambda n: inner
outer(0)
<function <lambdaat 0x00A01170>
>>>a=outer(0)
b=outer(1)
a()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
NameError: global name 'n' is not defined

Why doesn't 'inner' know it's been used in two different scopes, and
look up 'n' based on the one it's in?
That would be dynamic rather than lexical scoping.

Sep 28 '08 #7
On Sun, 28 Sep 2008 17:47:44 -0400, Terry Reedy wrote:
Aaron "Castironpi " Brady wrote:
>On Sep 28, 2:52 am, Steven D'Aprano <st...@REMOVE-THIS-
>>As for why the complicated version works, it may be clearer if you
expand it from a one-liner:

# expand: f[ n ]= (lambda n: ( lambda: n ) )( n )

inner = lambda: n
outer = lambda n: inner
f[n] = outer(n)

outer(0) =inner with a local scope of n=0 outer(1) =inner with a
local scope of n=1 etc.

For this to work, the 'expansion' has to be mental and not actual. Which
is to say, inner must be a text macro to be substituted back into outer.
Er, yes, that's what I meant, sorry for not being more explicit. That's
why it wasn't a copy and paste of actual running code.

Or perhaps I just confused myself and was talking nonsense.

--
Steven
Sep 28 '08 #8
On Sep 28, 4:47*pm, Terry Reedy <tjre...@udel.e duwrote:
Aaron "Castironpi " Brady wrote:
On Sep 28, 2:52 am, Steven D'Aprano <st...@REMOVE-THIS-
As for why the complicated version works, it may be clearer if you expand
it from a one-liner:
# expand: f[ n ]= (lambda n: ( lambda: n ) )( n )
inner = lambda: n
outer = lambda n: inner
f[n] = outer(n)
outer(0) =inner with a local scope of n=0
outer(1) =inner with a local scope of n=1 etc.

For this to work, the 'expansion' has to be mental and not actual.
Which is to say, inner must be a text macro to be substituted back into
outer.
Then, later, when you call inner() it grabs the local scope and returns
the number you expected.
I must have misunderstood. *Here's my run of your code:

I cannot speak to what Steven meant, but
>>inner = lambda: n

when inner is actually compiled outside of outer, it is no longer a
closure over outer's 'n' and 'n' will be looked for in globals instead.
>>outer = lambda n: inner
outer(0)
<function <lambdaat 0x00A01170>
>>a=outer(0)
b=outer(1)
a()
Traceback (most recent call last):
* File "<stdin>", line 1, in <module>
* File "<stdin>", line 1, in <lambda>
NameError: global name 'n' is not defined
Why doesn't 'inner' know it's been used in two different scopes, and
look up 'n' based on the one it's in?

That would be dynamic rather than lexical scoping.
I couldn't find how those apply on the wikipedia website. It says:
"dynamic scoping can be dangerous and almost no modern languages use
it", but it sounded like that was what closures use. Or maybe it was
what 'inner' in Steven's example would use. I'm confused.

Actually, I'll pick this apart a little bit. See above when I
suggested 'late' and 'early' functions which control (or simulate)
different bindings. I get the idea that 'late' bound functions would
use a dangerous "dynamic scope", but I could be wrong; that's just my
impression.
inner = lambda: n
outer = lambda n: inner
f[n] = outer(n)
outer(0) =inner with a local scope of n=0
outer(1) =inner with a local scope of n=1 etc.
If you defined these as:

inner= late( lambda: n )
outer= lambda n: inner

You could get the right results. It's not even clear you need
quotes. Perhaps 'late' could carry the definition of 'n' with it when
it's returned from 'outer'.

In my proposal, it makes a copy of the "localest" namespace, at least
all the variables used below it, then returns its argument in an
original closure.
Sep 28 '08 #9
Aaron "Castironpi " Brady wrote:
On Sep 28, 4:47 pm, Terry Reedy <tjre...@udel.e duwrote:
>Aaron "Castironpi " Brady wrote:
>>>>>inner = lambda: n
when inner is actually compiled outside of outer, it is no longer a
closure over outer's 'n' and 'n' will be looked for in globals instead.
>>>>>outer = lambda n: inner
>outer(0)
<function <lambdaat 0x00A01170>
>a=outer( 0)
>b=outer( 1)
>a()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
NameError: global name 'n' is not defined
Why doesn't 'inner' know it's been used in two different scopes, and
look up 'n' based on the one it's in?
That would be dynamic rather than lexical scoping.

I couldn't find how those apply on the wikipedia website. It says:
"dynamic scoping can be dangerous and almost no modern languages use
it", but it sounded like that was what closures use. Or maybe it was
what 'inner' in Steven's example would use. I'm confused.
As I understand it, partly from postings here years ago...

Lexical: The namespace scope of 'n' in inner is determined by where
inner is located in the code -- where is is compiled. This is Python
(and nearly all modern languages). Even without closures, the global
scope of a function is the module it is defined in.

Dynamic: The namespace scope of 'n' in inner, how it is looked up, is
determined by where inner is called from. This is what you seemed to be
suggesting -- look up 'n' based on the scope it is *used* in.

Even without closures, dynamic scoping would be if the global scope of a
function for each call were the module it is called in.

tjr

Sep 29 '08 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

14
1578
by: Alexander May | last post by:
When I define a function in the body of a loop, why doesn't the function "close" on the loop vairable? See example below. Thanks, Alex C:\Documents and Settings\Alexander May>python Python 2.3.3 (#51, Dec 18 2003, 20:22:39) on win32 Type "help", "copyright", "credits" or "license" for more information.
5
1722
by: paolo veronelli | last post by:
I've a vague idea of the differences,I don't know scheme anyway. I'd like to see an example to show what is missing in python about closures and possibly understand if ruby is better in this sense. Iuse ruby and python in parallel for my job just to learn them and their differences and python is shorter and cleaner ,but i feel it's missing something, in closures.Any hints?
2
3431
by: festiv | last post by:
Hi there, I want to learn how the compiler is implementing the dynamic binding. where can i read about this subject (the hole process). thanks.
9
2634
by: Gibby Koldenhof | last post by:
Hiya, Terrible subject but I haven't got a better term at the moment. I've been building up my own library of functionality (all nice conforming ISO C) for over 6 years and decided to adopt a more OO approach to fit my needs. Altough I used an OO approach previously for some subparts of the library it became somewhat difficult to maintain all those parts since they really are the same thing coded for each part (code duplication). So...
2
3059
by: Jake Barnes | last post by:
Using javascript closures to create singletons to ensure the survival of a reference to an HTML block when removeChild() may remove the last reference to the block and thus destory the block is what I'm hoping to achieve. I've never before had to use Javascript closures, but now I do, so I'm making an effort to understand them. I've been giving this essay a re-read: http://jibbering.com/faq/faq_notes/closures.html
2
9672
by: 09876 | last post by:
Hi: all I understand the difference between dynamic binding and static binding. But I just wonder what is the point to make the distinction between the dynamic binding and static binding. For example, in c, if a function pointer is used. say something like that float (*pt2Func)(float, float); so the compiler doesn't know what is pt2Func to be called until at run-time. Is it dynamic binding? Thanks.
14
2180
by: Khookie | last post by:
Woah... is it just me or do C programmers don't bother talking about how cool C can be (compared to Lisp, Haskell, etc.) - functionally speaking? // Lexical scoping - via nested functions #include <stdio.h> int main() { int x = 10;
40
1866
by: MartinRinehart | last post by:
I've rewritten a short article explaining closures in JavaScript. It's at: http://www.martinrinehart.com/articles/javascript-closures.html A big Thank You to PointedEars and Jorge for helping me get closer to the truth.
0
9489
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9298
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10072
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
8737
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7286
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6562
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5172
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5329
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
3
2698
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.