nested function scopes

Scott David Daniels Scott.Daniels at Acm.Org
Fri Dec 12 10:07:51 EST 2003


Nils Grimsmo wrote:
> i'm having some trouble nesting functions. consider the following:

You understand the rules, I think.  You just don't like them.  The
appropriate koan is "explicit is better than implicit".

I'd use something like this:

     class Struct(object):
         pass
     ...
     def outer():
         shared = Struct()
         shared.counter = 1
         def inner():
             print 'inner says:', shared.counter
             shared.counter += 1
         inner()
         inner()
         print 'outer says:', shared.counter

 > it would be very handy to be able to do this if i have nested
 > functions that use a lot of variables. only passing the variables
 > you assign to as packed compound parameters is a bit ugly,
 > since what subset of all variables this is might change.

Python's goal is to make code more easy to _read_, not _write_.
I find it easier to read the outer function when I know
what might change on me.  As my example shows, you needn't
thread all calls with the list of values.

There is another python idea here you haven't yet caught:
You speak of variables, but python only has associations of
names with values.  When you think in terms of names and
associations, you may find 'inner's ability to see what 'outer'
is talking about without being able to affect 'outer's
associations reasonable.

> i cannot say i like the python scope rules yet. they probably are
> practical, but they seem complicated an unstructured to me.
Give it time.  They are simple when you twist your head just right,
then they will seem completely simple.

> class C:
>     y = 0
>     def f(self):
>         print y

Again, "explicit is better than implicit".

Either:
     class D(object):
         y = "D's y"
         def f(self):
             print self.y
Or:
     class E(object):
         y = "E's y"
         def f(self):
             print E.y

behave as you wish.  Bindings first look to the instance and
then the class.  Python is quite dynamic.  There is, by the way,
a difference between the two above.  Ponder the following two
subclasses:

     class F(D):
         y = "F(D)'s y"

     class G(E):
         y = "G(E)'s y"

F().f() prints   F(D)'s y
G().f() prints   E's y

to work in the same was as the two above, but you'd be wrong.

> In the previous example, with the functions h() and g(), g() 
> why should not f() take y from C?
but which class should it choose in my examples?  The instance's
exact class?  The class in which the method was defined?  You
could mean either, and be without a way to specify the other.

Further, you could do this:
     y = "global"
     class C:
         def f(self):
             print y

     o = C()
     o.f()
     C.y = "classs variable"
     o.f()

Should the second call to o.f() print something different than
the first one?

Anyhow, this is really too long already.  I'll just include the
Struct definition I actually use when coding.  It uses too much
magic to be clear, but it prints nicely in interactive mode.

This in my "toys.py":

     class Struct(object):
         def __init__(self, **kw):
             self.__dict__.update(kw)
         def __repr__(self):
             return '%s(%s)' % (self.__class__.__name__,
                     ', '.join(['%s=%r' % name_val for
                          name_val in self.__dict__.items()]))

Then (when I want data types):
     import toys
     class Holder(toys.Struct): pass # just to give a name

And my example becomes:

     def outer():
         shared = Holder(counter=1, name='Fred')
         def inner():
             print 'inner says:', shared
             shared.counter += 1
         inner()
         inner()
         print 'outer says:', shared


-Scott David Daniels
Scott.Daniels at Acm.Org




More information about the Python-list mailing list