[Tutor] class variables

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Thu Jun 24 16:40:13 EDT 2004



On Wed, 23 Jun 2004, Nick Lunt wrote:

> Magnus and Danny, thankyou very much for your explanations.
>
> >From what you've both told me, it would seem that putting a local variable
> in the __init__ method of a class is quite pointless, ie
>
> example 1
> >>>class beer:
> >>>	def __init__(self):
> >>>		cans = 4
> >>>		self.make = 'murphys'


Hi Nick,

It's not exactly pointless to use local variables.  In the example above,
it's pointless, though.  *grin*


But here's a more concrete example where having a local variable might
help readability:


###
class NameMaker:
    """A class to make poor variable names."""
    def __init__(self, prefix):
        self.prefix = prefix
        self.counter = 0
    def makeName(self):
        nextName = "%s%s" % (self.prefix, self.counter)
        self.counter = self.counter + 1
        return nextName
###


Here's how it works:

###
>>> namer = NameMaker("x")
>>> namer.makeName()
'x0'
>>> namer.makeName()
'x1'
>>> namer.makeName()
'x2'
###


The use of 'nextName' as a local variable is fine here, just as long as we
understand that it's not part of the state of the instance.



> I admit Im quite confused by this still. Many python programs Ive
> studied mix up class variable with and without the 'instance/self'
> prefix.

In Java, the scope of a variable is very implicit.  The example we cooked
up above could be written in several ways:

/*** Java ***/
class NameMaker {
    private String prefix;
    private int counter;

    public NameMaker(String prefix) {
        this.prefix = prefix;
    }

    public String makeName() {
        String nextName = this.prefix + this.counter;
        this.counter = this.counter + 1;
        return nextName;
    }
}
/******/


In this Java example, all the variables are explicitely scoped.  But we
could just as easily have written this:


/*** Java ***/
class NameMaker {
    private String prefix;
    private int counter;

    public NameMaker(String p) {
        prefix = p;
    }

    public String makeName() {
        String nextName = prefix + counter;
        counter = counter + 1;
        return nextName;
    }
}
/******/


Python enforces an explicit coding style that makes it clear what's an
instance attribute and what's a local variable.  If we see the following
Python snippet:

        self.counter = self.counter + 1
        return nextName

in isolation, it's pretty clear what things are accessing the object
state.


But if we try the same experiement in Java:

        counter = counter + 1;
        return nextName;

then it's not as immediately clear what the scope of each variable is,
without looking at the immediate context.


In Java, we're often required to look at the context of the snippet to
figure out what things are local variables, and what things are instance
attributes.  In Python, we're forced to encode that knowledge more
explicitely.  It means that we type a little bit more, but we gain a lot
in code clarity.


[Digression: And it means that certain stupid scoping bugs just aren't
possible in Python.  For example, we can easily mess up the Java
constructor:

    public NameMaker(String prefix) {
        prefix = prefix;
    }

This compiles, but of course it's doing the wrong thing.  In contrast,
there's less of a danger of making this kind of error in Python, since all
object state change has that 'self' attribute.  Consistancy in variable
name access is a good thing.]



> Im also confused about whether I should put my class variable inside a
> method and use the self prefix, leave them outside of a method with no
> prefix, or leave them outside of a method but give them a prefix.

Ah!  What you've found is the equivalent of class attributes, or in Java
terminology, "static" variables.  When we say something like:

###
>>> class NameMaker2:
...     counter = 0
...     def __init__(self, prefix):
...         self.prefix = prefix
...     def makeName(self):
...         nextName = "%s%s" % (self.prefix, NameMaker2.counter)
...         NameMaker2.counter += 1
...         return nextName
...
>>> xmaker = NameMaker2('x')
>>> ymaker = NameMaker2('y')
>>> xmaker.makeName()
'x0'
>>> xmaker.makeName()
'x1'
>>> xmaker.makeName()
'x2'
>>> ymaker.makeName()
'y3'
###

then we are defining an attribute that is bound to the class alone, and
not to individual instances of the class.  We can see in NameMaker2 that
the 'counter' is being shared by both the 'xmaker' and 'ymaker' instances.


Does this make sense so far?  Please feel free to continue asking
questions about this.  Hope this helps!




More information about the Tutor mailing list