Style question - defining immutable class data members

Maxim Khitrov mkhitrov at gmail.com
Sat Mar 14 22:43:42 CET 2009


On Sat, Mar 14, 2009 at 4:31 PM, Gary Herron <gherron at islandtraining.com> wrote:
>> Perhaps a different example would help explain what I'm trying to do:
>>
>> class Case1(object):
>>        def __init__(self):
>>                self.count = 0
>>                self.list  = []
>>
>>        def inc(self):
>>                self.count += 1
>>                self.list.append(self.count)
>>
>>        def val(self):
>>                return (self.count, self.list)
>>
>> class Case2(object):
>>        count = 0
>>        list  = []
>>
>>        def inc(self):
>>                self.count += 1
>>                self.list.append(self.count)
>>
>>        def val(self):
>>                return (self.count, self.list)
>>
>> for i in xrange(10):
>>        c1 = Case1()
>>        c2 = Case2()
>>
>>        for j in xrange(i):
>>                c1.inc()
>>                c2.inc()
>>
>>        v1, l1 = c1.val()
>>        v2, l2 = c2.val()
>>
>>        print v1 == v2, l1 == l2
>>
>> The only difference between Case1 and Case2 classes is where the count
>> and list attributes are defined. You will notice that for an immutable
>> type (count), this doesn't matter. On the last line, v1 == v2 is
>> always True. When the type is mutable (list), you must define it in
>> __init__. This isn't about class attributes or shared instance
>> attributes/constants. This is about a small optimization in defining
>> per-instance variables. This optimization only applies to immutable
>> types.
>>
>> - Max
>> --
>> http://mail.python.org/mailman/listinfo/python-list
>>
>
> But now you are not listening to what people are telling you.  It has
> *nothing* to do with the mutability/immutability of the integer and the list
> your two classes create.
>
> The difference is this:
>
>   For C1:  You create 10 instances of C1.  Each one creates its own  count,
> and a list variables, and manipulates them calls to inc and val.  Then each
> on is discarded as you go through the next pass on the outer loop.

Correct, though the discarded part makes no difference.

>   For C2;  You create 10 instances of C2, but these 10 instances each
> manipulate values created once in the class itself.  The values manipulated
> by one instance of C2 in one pass through the loop are not affected when, on
> the next pass through the loop, that instance is destroyed and another
> instance is created.
> So...

Incorrect. Only the count is unaffected, which was the whole point of
my question.

>  If you want a variable that records/supplies some value across *all*
> instances of a class, use a class variable.  (Or use a global variable -- it
> would have the same effect.)
>
>  If you want a variable whose value is unique to each instance of a class,
> then make it an instance variable.
>
> Gary Herron

I never thought that such simple question would turn into this. David
Stanek gave me the answer I was looking for (thank you). You, on the
other hand, are still going after the wrong issue. Once again, here's
the same example using Terry's suggestions. No class instances are
being destroyed until the very end.

class Case1(object):
       def __init__(self):
               self.count = 0
               self.list  = []

       def inc(self):
               self.count += 1
               self.list.append(self.count)

       def val(self):
               return (self.count, self.list)

class Case2(object):
       count = 0
       list  = []

       def inc(self):
               self.count += 1
               self.list.append(self.count)

       def val(self):
               return (self.count, self.list)

c1a, c1b = Case1(), Case1()
c2a, c2b = Case2(), Case2()

c1a.inc(), c1b.inc()
c2a.inc(), c2b.inc()

print c1a.val(), c1b.val(), c2a.val(), c2b.val()

And the output:
(1, [1]), (1, [1]), (1, [1, 1]), (1, [1, 1])

The first element of every tuple is the same. This is the count, which
is immutable. The second element is not the same for c2[a,b]. This is
the list, which is mutable. My question was about immutable values,
and for those the who cases are identical. In the second case, c2a and
c2b begin with count referring to the same '0' object. This is where
the time and space savings are made. But because that object is
immutable, when += 1 operation is performed, a copy is made for each
instance. At that point, I am not sharing any values between class
instances.

The whole point is that this is a quick(er) way of providing initial
values for class instance variables when those values are immutable.
When/if that initial value is changed, a copy is made. In Case1, that
copy is made from the very begging in __init__. Please try to
understand what the question is about before responding to it.

- Max



More information about the Python-list mailing list