Maxim Khitrov wrote:
On Sat, Mar 14, 2009 at 4:31 PM, Gary Herron <gher...@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.


No, you are still misinterpreting your results. But you can be forgiven because this is quite a subtle point about Python's attribute access.

Here's how it works:

On access, self.count (or self.anything) attempts to find "count" in the instance variable "self". If that fails, it attempts to lookup "count" in the class. If that fails it attempts to lookup "count" in a global context, ...). However, on assignment self.count=... *must* necessarily mean the local instance variable -- no defaulting to a class variable on assignment.

So ...
With your class C2, the line
  self.count +=1
which really translates into
  self.count = self.count + 1
has two different actions depending on the pass through the inner loop.
The first time through (when no instance variable "count" exists), that line means <create and assign instance variable "count"> = <class variable count> + 1
and succeeding passes through (now that self.count is defined) become
   <instance count> = <instance count> +1
which is now the same as your class C1

To complete the subtlety, if you were to do
  self.list = self.list + [i]
you would see list behaving just as count.  However since,
   list.append(...)
is an in-place operation, and not an assignment, the creation of an instance variable is not provoked, and so all instances continue using the single class variable.

There is a lesson to be learned here.

Class variables can't be the target of an assignment through self, because that creates an instance variable. You can, however, assign to a class variable through
the class:
 C2.count = ...




 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

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to