accumulator generators

C

Cameron

I was reading this <a href="this http://www.paulgraham.com/icad.html">Paul
Graham article</a> and 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>
 
D

Diez B. Roggisch

Cameron said:
I was reading this <a href="this http://www.paulgraham.com/icad.html">Paul
Graham article</a> and 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
 
C

Cameron

Cameron schrieb:


I was reading this <a href="thishttp://www.paulgraham.com/icad.html">Paul
Graham article</a> and 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
 
H

Hans Nowak

Cameron said:
Cameron schrieb:


I was reading this <a href="thishttp://www.paulgraham.com/icad.html">Paul
Graham article</a> and 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).
 
A

Arnaud Delobelle

Cameron said:
I was reading this <a href="this http://www.paulgraham.com/icad.html">Paul
Graham article</a> and 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!
'spam'

But:
Traceback (most recent call last):
 
G

George Sakkis

Cameron said:
I was reading this <a href="thishttp://www.paulgraham.com/icad.html">Paul
Graham article</a> and 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!
'spam'

But:

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
 
A

Arnaud Delobelle

George Sakkis said:
Cameron said:
I was reading this <a href="thishttp://www.paulgraham.com/icad.html">Paul
Graham article</a> and 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'

'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.
 

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,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top