473,396 Members | 2,030 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

A gotcha: Python pain point?

Consider this example:
>>def funcs(x):
... for i in range(5):
... def g(): return x + i
... yield g

I would expect the value of x used in g to be that at the function
declaration time, as if you've pass g a (x=x) argument, especially
after reading this post: http://lua-users.org/wiki/LuaScopingDiscussion

But:
>>[ fun() for fun in list(funcs(1)) ]
[5, 5, 5, 5, 5]

Whereas:
>>[ fun() for fun in funcs(1) ]
[1, 2, 3, 4, 5]

This came up while discussing Python pain points at
http://intertwingly.net/blog/2007/06...ts#c1181602242

I can see how it works now, but I haven't found an easy-to-read
documentation on this.

I guess it's debatable if perhaps every i used in the loop shouldn't
be a different i. It had me fooled, anyways.

Rgds,
Bjorn

Jun 11 '07 #1
7 1284
Beorn wrote:
Consider this example:
>>def funcs(x):
... for i in range(5):
... def g(): return x + i
... yield g

I would expect the value of x used in g to be that at the function
declaration time, as if you've pass g a (x=x) argument, especially
after reading this post: http://lua-users.org/wiki/LuaScopingDiscussion

But:
>>[ fun() for fun in list(funcs(1)) ]
[5, 5, 5, 5, 5]

Whereas:
>>[ fun() for fun in funcs(1) ]
[1, 2, 3, 4, 5]

This came up while discussing Python pain points at
http://intertwingly.net/blog/2007/06...ts#c1181602242

I can see how it works now, but I haven't found an easy-to-read
documentation on this.

I guess it's debatable if perhaps every i used in the loop shouldn't
be a different i. It had me fooled, anyways.

Rgds,
Bjorn
If this isn't classified as a bug, then someone has some serious
explaining to do. Why would it be desirable for a generator to behave
differently in two different contexts. Should I import this to see how
many principles this behavior violates?

James
Jun 11 '07 #2

"Beorn" <bj****@gmail.comwrote in message
news:11**********************@z28g2000prd.googlegr oups.com...
| Consider this example:
|
| >>def funcs(x):
| ... for i in range(5):
| ... def g(): return x + i
| ... yield g
|
| I would expect the value of x used in g to be that at the function
| declaration time, as if you've pass g a (x=x) argument,

Since x is constant during the funcs call, does not matter. Perhaps you
meant i, which does vary? If so, put i=i in the header -- or perhaps ii=i
to give two different names to two things which are made to have the same
value.

In any case, the simple rule is that default argument expressions in the
*header* are evaluated at definition time while the *body* (past the doc
string, if any) is executed after a call.

Some people expect defaults to be executed every call; others expect part
of the body to be executed once. Both get in trouble. For nested
functions, outer call time is inner definition time and this can confuses.
Defining (but delaying the call of) multiple identical inner functions, as
you do in the first example below, also confuses.

| especially
| after reading this post: http://lua-users.org/wiki/LuaScopingDiscussion

Lua is not Python.

| But:
|
| >>[ fun() for fun in list(funcs(1)) ]
| [5, 5, 5, 5, 5]
|
| Whereas:
|
| >>[ fun() for fun in funcs(1) ]
| [1, 2, 3, 4, 5]

As Calderone explained, the simple rule works as long as one keeps track of
what is called when.

Terry Jan Reedy

Jun 12 '07 #3
James Stroud wrote:
Beorn wrote:
>Consider this example:
> >>def funcs(x):
... for i in range(5):
... def g(): return x + i
... yield g

I would expect the value of x used in g to be that at the function
declaration time, as if you've pass g a (x=x) argument, especially
after reading this post: http://lua-users.org/wiki/LuaScopingDiscussion

But:
> >>[ fun() for fun in list(funcs(1)) ]
[5, 5, 5, 5, 5]

Whereas:
> >>[ fun() for fun in funcs(1) ]
[1, 2, 3, 4, 5]

This came up while discussing Python pain points at
http://intertwingly.net/blog/2007/06...ts#c1181602242

I can see how it works now, but I haven't found an easy-to-read
documentation on this.

I guess it's debatable if perhaps every i used in the loop shouldn't
be a different i. It had me fooled, anyways.

Rgds,
Bjorn

If this isn't classified as a bug, then someone has some serious
explaining to do. Why would it be desirable for a generator to behave
differently in two different contexts. Should I import this to see how
many principles this behavior violates?
It's no bug. The generator behaves the same way in both cases. What is
different is the calling time of the function.

Certainly a surprising outcome. But no bug.

Diez
Jun 12 '07 #4

"James Stroud" <js*****@mbi.ucla.eduwrote in message
news:f4**********@zinnia.noc.ucla.edu...
| Beorn wrote:
| Consider this example:
| >
| >>def funcs(x):
| ... for i in range(5):
| ... def g(): return x + i
| ... yield g
| >
| >>[ fun() for fun in list(funcs(1)) ]
| [5, 5, 5, 5, 5]
| >
| Whereas:
| >
| >>[ fun() for fun in funcs(1) ]
| [1, 2, 3, 4, 5]

| If this isn't classified as a bug,

It is not, it is well-documented behavior. Still, suggestions for
improvement might be considered.

| Why would it be desirable for a generator to behave
| differently in two different contexts.

I have no idea.

Each call of the generator function funcs behaves the same. It returns a
generator that yields 5 identical copies of the inner function g. The
multiple copies are not needed and only serve to confuse the issue.
Changing funcs to return a generator that yields the *same* function (five
times) gives the same behavior.

def funcs(x):
def g(): return x + i
for i in range(5):
yield g

print [ fun() for fun in list(funcs(1)) ]
print [ fun() for fun in funcs(1) ]
>># when run
[5, 5, 5, 5, 5]
[1, 2, 3, 4, 5]

What matters is the value of g's nonlocal var i (funcs' local var i) when
the yielded function g is *called*.

The difference between returning versus yielding an inner closure such as g
is this. If g is returned, the outer function has terminated and the
enclosed variable(s), i in this case, is frozen at its final value. If g
is yielded, the enclosed i is *live* as long as the generator is, and its
values can change between calls, as in the second print statement.

| Should I import this to see how
| many principles this behavior violates?

???

If you meant 'report' (on SF), please do not.

Terry Jan Reedy

Jun 12 '07 #5
Beorn wrote:
Consider this example:
>>def funcs(x):
... for i in range(5):
... def g(): return x + i
... yield g

I would expect the value of x used in g to be that at the function
You mean i here, don't you?
declaration time, as if you've pass g a (x=x) argument, especially
after reading this post: http://lua-users.org/wiki/LuaScopingDiscussion

But:
>>[ fun() for fun in list(funcs(1)) ]
[5, 5, 5, 5, 5]

Whereas:
>>[ fun() for fun in funcs(1) ]
[1, 2, 3, 4, 5]

This came up while discussing Python pain points at
http://intertwingly.net/blog/2007/06...ts#c1181602242

I can see how it works now, but I haven't found an easy-to-read
documentation on this.
This has been discussed here very often. Python closures do capture the
names, not the values. If you want a value at a certain point, you need to
bind in the generated function scope by passing it as parameter. Like this:

def funcs(x):
for i in xrange(5):
def g(i=i): return x + i
yield g

Diez
Jun 12 '07 #6
In <ma***************************************@python. org>, Terry Reedy
wrote:
| Should I import this to see how
| many principles this behavior violates?

???

If you meant 'report' (on SF), please do not.
I think he meant ``import this`` at the Python interpreter.

Ciao,
Marc 'BlackJack' Rintsch
Jun 12 '07 #7
On Jun 12, 5:00 pm, "Diez B. Roggisch" <d...@nospam.web.dewrote:
Beorn wrote:
[...]
I can see how it works now, but I haven't found an easy-to-read
documentation on this.

This has been discussed here very often. Python closures do capture the
names, not the values. If you want a value at a certain point, you need to
bind in the generated function scope by passing it as parameter. Like this:

def funcs(x):
for i in xrange(5):
def g(i=i): return x + i
yield g
Well, I would hope to see this documented somewhere prominently;
looking at the PEPs didn't give me much of a clue. I'm not even sure
how to describe the problem, so formulating search queries becomes
hard(!).

How does other languages, like Ruby, work? Sam (see original link
about pain points) was very surprised that the for loop didn't
introduce a new scope (is that the right word?) for each iteration?

Rgds,
Bjorn

Jun 13 '07 #8

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

Similar topics

220
by: Brandon J. Van Every | last post by:
What's better about Ruby than Python? I'm sure there's something. What is it? This is not a troll. I'm language shopping and I want people's answers. I don't know beans about Ruby or have...
8
by: eScrewDotCom | last post by:
eScrew Welcome to eScrew! eScrew is eScrew and this is eScrew story. eScrew will tell you eScrew story if you promise eScrew to consider eScrew story as joke. eScrew story is very funny. eScrew...
112
by: mystilleef | last post by:
Hello, What is the Pythonic way of implementing getters and setters. I've heard people say the use of accessors is not Pythonic. But why? And what is the alternative? I refrain from using them...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
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,...
0
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...
0
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,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
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,...

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.