Style question - defining immutable class data members

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Sun Mar 15 16:30:21 CET 2009


Maxim Khitrov a écrit :
> On Sat, Mar 14, 2009 at 2:07 PM, Gary Herron <gherron at islandtraining.com> wrote:
>> Maxim Khitrov wrote:
>>> Very simple question on the preferred coding style. I frequently write
>>> classes that have some data members initialized to immutable values.
>>> For example:
>>>
>>> class Test(object):
>>>    def __init__(self):
>>>        self.some_value = 0
>>>        self.another_value = None
>>>
>>> Similar effect can be achieved by defining some_value and
>>> another_value for the entire class, like so:
>>>
>>> class Test(object):
>>>    some_value = 0
>>>    another_value = None
>>>
>>> The advantage of doing this is that the assignments are evaluated once
>>> and thus the creation of that class is a bit faster. Access is still
>>> performed through self.some_value and self.another_value. Is there a
>>> reason to prefer the first style over the second?
>>>
>>> - Max
>>> --
>>> http://mail.python.org/mailman/listinfo/python-list
>>>
>> Such things are often called class attributes, and the are fine.  If you
>> look through Python's standard library,  you will find many examples of
>> class attributes.
>>
>> However, you appear to have either a misuse or misconception of the word
>> "immutable' here.   Whether the value you assign to a class attribute is
>> mutable or immutable is irrelevant.   Also whether you plan on leaving the
>> value constant or not is also not relevant.
>> What does matter is this:  If every instance wants access to a single value
>> (immutable or not), use a class attribute, otherwise use an instance
>> attribute.
>>
>> Gary Herron
> 
> 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

This above statement does 3 things:

1/ read "self.count", which, *the first time this method is called*, 
resolves to self.__class__.count since by that time, there's no instance 
attribute named 'count'

2/ add one to the int object returned by looking up 'self.count'

3/ store the result as instance attribute named 'count'.

IOW, the first calls to the method reads the class 'count' attribute and 
creates the instance 'count' attribute. Subsequent calls will resolve 
'self.count' as the instance attribute since it now exists.


> 		self.list.append(self.count)

And this statement:

1/ read 'self.list', which resolves to self.__class__.list (the class 
attribute) since there's no instance attribute named 'list'

2/ mutate the class attribute

> 
> 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.

cf above.

> On the last line, v1 == v2 is
> always True. When the type is mutable (list), you must define it in
> __init__.

Try rebinding self.list instead of mutating it, and you'll find out that 
it's not related to immutable/mutable types, but to how Python's object 
model works wrt/ attribute lookup and attribute binding.

An simple example being worth a thousand words:

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

     def inc(self):
         print self.__dict__
         self.count += 1
         self.list = self.list + [self.count]
         print self.__dict__

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

 >>> c = Case3()
 >>> print c.__dict__
{}
 >>> print c.__class__.__dict__
{'count': 0, '__module__': '__main__', 'val': <function val at 
0xb7c7bd4c>, 'list': [], '__dict__': <attribute '__dict__' of 'Case3' 
objects>, '__weakref__': <attribute '__weakref__' of 'Case3' objects>, 
'__doc__': None, 'inc': <function inc at 0xb7c81844>}
 >>> c.inc()
{}
{'count': 1, 'list': [1]}
 >>> print c.__dict__
{'count': 1, 'list': [1]}
 >>> print c.__class__.__dict__
{'count': 0, '__module__': '__main__', 'val': <function val at 
0xb7c7bd4c>, 'list': [], '__dict__': <attribute '__dict__' of 'Case3' 
objects>, '__weakref__': <attribute '__weakref__' of 'Case3' objects>, 
'__doc__': None, 'inc': <function inc at 0xb7c81844>}
 >>>


> This isn't about class attributes or shared instance
> attributes/constants.

Yes, it is.

> This is about a small optimization in defining
> per-instance variables. This optimization only applies to immutable
> types.

Wrong conclusions, sorry.

HTH



More information about the Python-list mailing list