Oxfam America banner

Tuesday, October 28, 2008

Class Variables

My dreaded multiple inheritance article was published in Python Magazine yesterday. It was tricky enough giving a presentation on the subject, so I look at the article as somewhat of an achievement. Someone contacted me shortly after concerning my use of class variable in one of the examples:




class Publishable(object):
published = False
def __init__(self, start_date, end_date):
self.start_date = start_date
self.end_date = end_date
def publish(self):
self.published = True


The inquirer's question:


I've always been under the impression having class & instance
variables with the same name is just confusing and not a good way to
write Python. Why did you do this?



Anyhow, on the subject of "class variables," here are my thoughts:



[Begin email response]




I guess it depends on who you ask. The pattern of using class level
variable as a default values for instances is one I like and don't
find confusing. But these kind of things are always subjective. It's
not my own pattern either, but one I've noticed in other projects.



http://twistedmatrix.com/trac/browser/tags/releases/twisted-8.1.0/twisted/internet/defer.py#L137
http://trac.pythonpaste.org/pythonpaste/browser/Paste/WebOb/trunk/webob/__init__.py#L474


I make a distinction, though, between a "class variable" and a
variable defined at the class level. In terms of the VM, they are of
course the same thing, but are differentiated in human terms by uses
cases. The former "class variable" is useful if you are tracking
state on a class - for example keeping a count of objects created
through a class method:




class Foo:
_created = 0
@classmethod
def create(cls):
foo = Foo()
cls._created += 1
return foo



The latter "class level variable" can be useful for defining default
values for attributes (especially ones which cannot be specified in
the constructor):




class Task:
completed = False
def __init__(self):
self.start_time = time.time()



(This is same pattern I'm adopting in my article.) Of course, when
the "completed" attribute is looked up on an instance of Task, it has
to take an extra step - first looking in dict of instance and then in
the class. However, this also means a decent space optimization if
your ratio of incomplete tasks to complete tasks is very high - once a
task is marked as completed it is likely to get discarded and hence
garbage collected.




I think it's only confusing (again, this is very subjective) if you
try to mix the two uses cases in one class:




class Confusing:
completed = 0
def __init__(self):
self.completed = False
@classmethod
def complete(cls, confusingInstance):
cls.completed += 1
confusingInstance.completed = True


Thanks for the feedback.



[End email response]

2 comments:

kevingill said...

Hi Drew,

I just want to say I loved your article in pyMag. I have been a zope3 users for about a year, and while I knew how to use zope.interface, I wasn't aware of why. I always thought that it is a bit of overkill, but now I know better.

Great article. I hope you write more (preferably about Zope).

Thanks,

Kevin

djfroofy said...

@kevingill

Thanks for the comment. I'm glad you liked the article - I tried my best to make a dry subject interesting. :)

Unfortunately, my Zope knowledge is mostly tangential (and limited to zope.interface) through experience working with the Twisted framework. I do hope to write something on a specific library (rather than language concepts), but Zope is outside of my scope.

Post a Comment