[Python-Dev] closure semantics

Guido van Rossum guido at python.org
Wed Oct 22 13:57:18 EDT 2003


[Samuele]
> why exactly do we want write access to outer scopes?
> 
> for completeness, to avoid the overhead of introducing a class here
> and there, to facilitate people using Scheme textbooks with Python?

Probably the latter; I think Jeremy Hylton does know more Scheme than
I do. :-)

> so far I have not been missing it,
> 
> I don't find:
> 
> def accgen(n):
>    def acc(i):
>      global n in accgen
>      n += i
>      return n
>    return acc
> 
> particulary more compelling than:
> 
> class accgen:
>    def __init__(self, n):
>      self.n = n
> 
>    def __call__(self, i):
>      self.n += i
>      return self.n

Some people have "fear of classes".  Some people think that a
function's scope can be cheaper than an object (someone should time
this).

Looking at the last example in the itertools docs:

  def tee(iterable):
      "Return two independent iterators from a single iterable"
      def gen(next, data={}, cnt=[0]):
          dpop = data.pop
          for i in count():
              if i == cnt[0]:
                  item = data[i] = next()
                  cnt[0] += 1
              else:
                  item = dpop(i)
              yield item
      next = iter(iterable).next
      return (gen(next), gen(next))

This would have been clearer if the author didn't have to resort to
representing his counter variable as a list of one element.  Using
'global* x' to mean 'find x in an outer scope', and also moving data
into the outer scope, again to emphasize that it is shared between
multiple calls of gen() without abusing default arguments, it would
become:

  def tee(iterable):
      "Return two independent iterators from a single iterable"
      data = {}
      cnt = 0
      def gen(next):
          global* cnt
          dpop = data.pop
          for i in count():
              if i == cnt:
                  item = data[i] = next()
                  cnt += 1
              else:
                  item = dpop(i)
              yield item
      next = iter(iterable).next
      return (gen(next), gen(next))

which is IMO more readable.

But in 2.4 this will become a real object implemented in C. :-)

--Guido van Rossum (home page: http://www.python.org/~guido/)



More information about the Python-Dev mailing list