Re: class scope

Jim Roskind (jar@infoseek.com)
Mon, 25 Jul 1994 22:40:11 -0700

> Date: Mon, 25 Jul 1994 23:04:35 -0500 (GMT-0500)
> From: "David N. Richards" <richards@aaron.music.qc.edu>
>
> I am a bit confused with class scope (newbie). Given the
> following class definition:
>
> class abc:
> list = ['a', 'b', 'c']
> def printInfo(arg):
> x = 0
> while x < len(list): # <== this as you know will yield an error
> print type[x] # TypeError: len() of unsized object
> x = x + 1 # I know it works if I say len(abc.list)
>
> But isn't 'list' within scope of the class definition??

You are hitting on one of the hardest hurdles in transitioning from
stuff like C++ to Python (at least it was the biggest mental
transition for me). The concept of "scope" is typically used in C/C++
programs (and lots of other languages) to involve "lexical scope", or
stuff that was declared/defined prior to a point, and "near" in some
sense (example: within some curly braces). In Python, folks carry
around "run time dictionaries," wherein names are looked up
(basically, run time symbol tables). Although I originally saw this
as a hurdle to understand/overcome, I now tend to see this as a very
notable feature, so don't abandon ship (yet) if this all seems a bit
strange ;-).

There are two distinct dictionaries to consider: a local "stack frame
dictionary" and the "instance dictionary." There are also secondary
places (dictionaries) that Python looks if it can't find stuff right
away in each case.

In your example, there is a "dictionary" associated with the instance
of class "abc" called "arg" (most programs I've seen call this first
argument "self," but this is strictly a convention, not a mandate).
Since "arg" is an instance of a class, there are some interesting
"lookup" rules when members (a.k.a., slotnames, or method names) are
looked up in that dictionary.

For example, if you entered "arg.list" Python would *first* look in
the special dictionary containing only instance slots for the instance
"arg." From what you've shown in the above fragment, the local
dictionary for "arg" has *nothing*, so a fallback scheme is
used. (Note that typically some __init__() method sets up this local
instance dictionary). Since the instance dictionary is empty, the
fallback proceeds to looking in the dictionary for the entire class
(there is only one such dictionary for each class, and it has all the
method names listed in it). In this case, it would find the slot
defined in the class, called "list." This slot in the class
definition can be accessed either via "abc.list" or "arg.list". The
one caveat is that if *ever* an assignment is done to such a slot,
then these two ways of referring will suddenly be *very* different, and
have very different effects. To be specific, if you assigned to
"arg.list" then forever more (almost) there would be a special slot in
only this instance of "arg" that is called "list". Furthermore,
future lookups of "arg.list" would resolve immediately to this
instance-specific slot. On the other hand, if you assigned to
"abc.list," then all future references by any instance to either
"abc.list" or "arg.list" would get this new value (presuming we
haven't created any slots in the instance to get in the way). Exactly
where lookup proceeds when these places don't get results can be
learned from the Ref Man :-). Fundamentally, this first level of
lookup is critical to finding any method which is associated with an
instance. The special (weird) way of using it to create what a C
programmer might call (almost) "class static members" just happens to
fall out (and that is what you have done with "abc.list").

The second dictionary in this example is the "stack frame dictionary"
(I invented my words for this, so I'll probably be slammed by someone
with the *real* name ;-) ). Note that there is a *new* stack frame
dictionary created for each invocation of a function/method. This
dictionary provides a name space that is used to hold data that is
analogously held in local variables of type auto in C/C++. Whenever a
name is looked up out-of-the-blue (such as "list" in the above
example), then it is first looked for in the local stack frame
dictionary. The stack frame dictionary has *only* "x" in the above
example. Stack frame dictionaries contain (for the most part) only
variables that are assigned to anywhere in function/method. At
"compile time," a scan of a function reveals all names that need to be
placed into this local stack frame (if you don't want a special local
copy of a name, and you want to assign to it, you must use the
"global" statement). Anyway, the rules are simple for lookup of names
in functions. Python always looks in the "stack frame dictionary" and
if that fails, then the "module global dictionary," and as a final
fallback in some system dictionary. Hence the lexical position of
"list" as a member of class "abc" is *not* part of the lookup scheme.
In some sense, the "class hierarchy" is only significant to lookup for
members of that class. The lexical containment of a method (such as
printInfo()) in a class (such as "abc") plays *no* role in the lookup
of "out-of-the-blue" values during the execution of a method.

When you start thinking in terms of "dictionaries," and think less
often in terms of "scopes," then Python will start making a lot more
sense. A giant win for Python is that symbol resolution takes place
at run time, and hence Python is a very dynamic (rapid prototyping)
language. To achieve this very late binding (they only figure out
what a name refers to typically at run time!) Python needs these
dictionaries flying around. The *good* news is that there really
aren't that many different kinds of dictionaries, and the lookup rules
are pretty concise and consistent (IMHO). This means its easy to say,
easy to learn, and easy to use (once you're past the scope hurdle ;-)
).

> Must I use the 'abc.list' notation within the class definition
> or am I missing something?

In your case, you can either use "arg.list" or "abc.list" and probably
achieve equivalent effect. I suspect that you are not aware that as
you have defined it, there is only *one* list for *all* instances to
share. My guess is that you need to look into using an __init__()
method to set up lists within each instance, but I'm really not sure
what you are doing ;-). I *think* you were assuming that the class
definition was much like a struct definition in C, wherein member
names are enumerated. The shocker is that there are no definitions of
slot names done at compile time... it is all figured out on the fly at
run time.

You might also change the second line of the method from being:

print type[x]

to:

print type(arg.list[x]) # and avoid your next error ;-)

Lastly, if you wanted to code stuff like this, you might kick the
Fortran habit of iterating by index, and code it as:

class abc:
list = ['a', 'b', 'c']
def printInfo(arg):
for x in arg.list:
print type(x)

Jim

Jim Roskind
voice: 408.982.4469
fax: 408.986.1889
jar@infoseek.com