By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
439,957 Members | 1,960 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 439,957 IT Pros & Developers. It's quick & easy.

accumulator generators

P: n/a
I was reading this <a href="this http://www.paulgraham.com/icad.html">Paul
Graham article</aand he builds an accumuator generator function in
the appendix. His looks like this:

<pre>
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
</pre>

Why does that work, but not this:

<pre>
def foo(n):
s = n
def bar(i):
s += i
return s
return bar
</pre>
Jun 27 '08 #1
Share this Question
Share on Google+
7 Replies


P: n/a
Cameron schrieb:
I was reading this <a href="this http://www.paulgraham.com/icad.html">Paul
Graham article</aand he builds an accumuator generator function in
the appendix. His looks like this:

<pre>
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
</pre>

Why does that work, but not this:

<pre>
def foo(n):
s = n
def bar(i):
s += i
return s
return bar
</pre>
Because python's static analysis infers s as being a variable local to
bar in the second case - so you can't modify it in the outer scope.

In the future, you may declare

def bar(i):
nonlocal s
...
Diez
Jun 27 '08 #2

P: n/a
At 2008-05-30T19:50:43Z, Cameron <ca**********@gmail.comwrites:
Why does that work, but not this:

def foo(n):
s = n
def bar(i):
s += i
return s
return bar
Assume that n is an int, making s one also. Ints are immutable; you can
only copy them. So your bar is taking s, adding i to it, then assigning the
value back to the local variable named s.
--
Kirk Strauser
The Day Companies
Jun 27 '08 #3

P: n/a
On May 30, 1:04*pm, "Diez B. Roggisch" <de...@nospam.web.dewrote:
Cameron schrieb:
I was reading this <a href="thishttp://www.paulgraham.com/icad.html">Paul
Graham article</aand he builds an accumuator generator function in
the appendix. His looks like this:
<pre>
def foo(n):
* s = [n]
* def bar(i):
* * s[0] += i
* * return s[0]
* return bar
</pre>
Why does that work, but not this:
<pre>
def foo(n):
* s = n
* def bar(i):
* * s += i
* * return s
* return bar
</pre>

Because python's static analysis infers s as being a variable local to
bar in the second case - so you can't modify it in the outer scope.

In the future, you may declare

def bar(i):
* * *nonlocal s
* * *...

Diez
thanks for the response. Just to make sure I understand- Is the reason
it works in the first case because s[0] is undefined at that point (in
bar), and so python looks in the outer scope and finds it there?

Cameron

Jun 27 '08 #4

P: n/a
Cameron wrote:
On May 30, 1:04 pm, "Diez B. Roggisch" <de...@nospam.web.dewrote:
>Cameron schrieb:
>>I was reading this <a href="thishttp://www.paulgraham.com/icad.html">Paul
Graham article</aand he builds an accumuator generator function in
the appendix. His looks like this:
<pre>
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
</pre>
Why does that work, but not this:
<pre>
def foo(n):
s = n
def bar(i):
s += i
return s
return bar
</pre>
Because python's static analysis infers s as being a variable local to
bar in the second case - so you can't modify it in the outer scope.

In the future, you may declare

def bar(i):
nonlocal s
...

Diez

thanks for the response. Just to make sure I understand- Is the reason
it works in the first case because s[0] is undefined at that point (in
bar), and so python looks in the outer scope and finds it there?
You can refer to variables in enclosing scopes, just not redefine them in that
same scope. That's why in the first example, bar can refer to to s (defined in
foo). By assigning to s[0], it modifies the list, which is OK; trying to
redefine the name 's' (like the second example tries to do) would not be OK.

Also see: http://zephyrfalcon.org/labs/python_pitfalls.html (pitfall #6).

--
Hans Nowak (zephyrfalcon at gmail dot com)
http://4.flowsnake.org/
Jun 27 '08 #5

P: n/a
Cameron <ca**********@gmail.comwrites:
I was reading this <a href="this http://www.paulgraham.com/icad.html">Paul
Graham article</aand he builds an accumuator generator function in
the appendix. His looks like this:

<pre>
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
</pre>

Why does that work, but not this:

<pre>
def foo(n):
s = n
def bar(i):
s += i
return s
return bar
</pre>
Others have explained why, but this looks like "pythonized LISP" to
me. I would rather use a generator function:

def foo(n):
while True:
n += yield n

Although the problem is that you can't send it values the first time
round!
>>bar = foo('s')
bar.next()
's'
>>bar.send('p')
'sp'
>>bar.send('am')
'spam'

But:
>>bar = foo(3)
bar.send(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
>>>
--
Arnaud
Jun 27 '08 #6

P: n/a
On May 31, 4:19*am, Arnaud Delobelle <arno...@googlemail.comwrote:
Cameron <cameronla...@gmail.comwrites:
I was reading this <a href="thishttp://www.paulgraham.com/icad.html">Paul
Graham article</aand he builds an accumuator generator function in
the appendix. His looks like this:
<pre>
def foo(n):
* s = [n]
* def bar(i):
* * s[0] += i
* * return s[0]
* return bar
</pre>
Why does that work, but not this:
<pre>
def foo(n):
* s = n
* def bar(i):
* * s += i
* * return s
* return bar
</pre>

Others have explained why, but this looks like "pythonized LISP" to
me. *I would rather use a generator function:

def foo(n):
* * while True:
* * * * n += yield n

Although the problem is that you can't send it values the first time
round!
>bar = foo('s')
bar.next()
's'
>bar.send('p')
'sp'
>bar.send('am')

'spam'

But:
>bar = foo(3)
bar.send(2)

Traceback (most recent call last):
* File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator
I find the "pythonized LISP" solution more understandable, even
without the initial next() requirement. YMMV

George
Jun 27 '08 #7

P: n/a
George Sakkis <ge***********@gmail.comwrites:
On May 31, 4:19*am, Arnaud Delobelle <arno...@googlemail.comwrote:
>Cameron <cameronla...@gmail.comwrites:
I was reading this <a href="thishttp://www.paulgraham.com/icad.html">Paul
Graham article</aand he builds an accumuator generator function in
the appendix. His looks like this:
<pre>
def foo(n):
* s = [n]
* def bar(i):
* * s[0] += i
* * return s[0]
* return bar
</pre>
Why does that work, but not this:
<pre>
def foo(n):
* s = n
* def bar(i):
* * s += i
* * return s
* return bar
</pre>

Others have explained why, but this looks like "pythonized LISP" to
me. *I would rather use a generator function:

def foo(n):
* * while True:
* * * * n += yield n

Although the problem is that you can't send it values the first time
round!
>>bar = foo('s')
bar.next()
's'
>>bar.send('p')
'sp'
>>bar.send('am')

'spam'

But:
>>bar = foo(3)
bar.send(2)

Traceback (most recent call last):
* File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator

I find the "pythonized LISP" solution more understandable, even
without the initial next() requirement. YMMV

George
In that case a class may be better? IMHO, what is such a natural
idiom in LISP does not translate well literally into Python.

class Foo(object):
def __init__(self, n):
self.s = n
def __call__(self, i)
self.s += i
return self.s

Anything to avoid the mutable container trick! Of course 'nonlocal'
takes care of this in py3k. I have missed the absence of 'nonlocal' a
lot, but now that it is around the corner, I grow less sure about it.

--
Arnaud
Jun 27 '08 #8

This discussion thread is closed

Replies have been disabled for this discussion.