David Perlman wrote:
OK, I thought I had this one fixed but it was weirder than I thought.  I think I understand what's going on, but I wanted to check with the experts here.

I have the following class definition, which does not subclass anything:

class oneStim:
    def __init__(self, time, mods=[], dur=None, format='%1.2f'):
        self.time=time
        self.mods=mods
        self.dur=dur
        self.format=format

    def __cmp__(self,other):
        return cmp(self.time,other.time)

    def __repr__(self):
        timestr=self.format % self.time
        if self.mods == []:
            modstr=''
        else:
            modstr = '*' + ','.join(self.format % i for i in self.mods)
        if self.dur == None:
            durstr = ''
        else:
            durstr = ':' + (self.format % self.dur)
        return timestr + modstr + durstr

    def __len__(self):
        return len(self.__repr__())


>>> a=oneStim(40)
>>> a
40.00
>>> a.mods.append(3)
>>> a
40.00*3.00
>>> a.dur=10
>>> a
40.00*3.00:10.00
>>> a.mods.append(1)
>>> a
40.00*3.00,1.00:10.00

So far so good, that's exactly what it's supposed to do.  But now look:

>>> b=oneStim(50)
>>> b
50.00*3.00,1.00

The mods that were added to the first instance of oneStim also appear in the second, newly created instance!

It appears that what is happening here is that the __init__() method is being parsed by the interpreter once at initial run, and at that time the statement "mods=[]" is being parsed, which means that the [] object is being instantiated once there at the beginning.  So every instantiation of class oneStim ends up sharing a reference to the same list object, instead of each one having its own.

I fixed this by changing it to "mods=None" and then setting it in the body of the __init__ method.  Works fine now.

My question is, is this just a quirky misbehavior, or is there a principled reason why the code I have shown above only instantiates the empty list in the arguments once?

RTFM: (Reference Manual, 7.6 Function definitions)
....
Default parameter values are evaluated when the function definition is executed. This means that the _expression_ is evaluated once, when the function is defined, and that that same ``pre-computed'' value is used for each call. This is especially important to understand when a default parameter is a mutable object, such as a list or a dictionary: if the function modifies the object (e.g. by appending an item to a list), the default value is in effect modified. This is generally not what was intended. A way around this is to use None as the default, and explicitly test for it in the body of the function, e.g.:

def whats_on_the_telly(penguin=None):
    if penguin is None:
        penguin = []
    penguin.append("property of the zoo")
    return penguin

Thanks for any insight.  As I said, I got it to work fine now, so this isn't critical, but I'm curious to understand why things work the way they do.  :)

--
-dave----------------------------------------------------------------
"Pseudo-colored pictures of a person's brain lighting up are
undoubtedly more persuasive than a pattern of squiggles produced by a
polygraph.  That could be a big problem if the goal is to get to the
truth."  -Dr. Steven Hyman, Harvard



_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor



--
Bob Gailer
Chapel Hill NC
919-636-4239


_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
http://mail.python.org/mailman/listinfo/tutor

Reply via email to