Recipe 20.13. Initializing Instance Attributes Without Using _ _init_ _
Credit: Dan Perl, Shalabh Chaturvedi
Problem
Your classes need to initialize some instance attributes when they
generate new instances. If you do the initialization, as normal, in
the _ _init_ _ method of your classes, then, when
anybody subclasses your classes, they must remember to invoke your
classes' _ _init_ _ methods. Your
classes often get subclassed by beginners who forget this elementary
requirement, and you're getting tired of the
resulting support requests. You'd like an approach
that beginners subclassing your classes are less likely to mess up.
Solution
Beginners are unlikely to have heard of the _ _new_
_ method, so you can place your initialization there,
instead of in _ _init_ _:
# a couple of classes that you write:
class super1(object):
def _ _new_ _(cls, *args, **kwargs):
obj = super(super1, cls)._ _new_ _(cls, *args, **kwargs)
obj.attr1 = [ ]
return obj
def _ _str_ _(self):
show_attr = [ ]
for attr, value in sorted(self._ _dict_ _.iteritems( )):
show_attr.append('%s:%r' % (attr, value))
return '%s with %s' % (self._ _class_ _._ _name_ _,
', '.join(show_attr))
class super2(object):
def _ _new_ _(cls, *args, **kwargs):
obj = super(super2, cls)._ _new_ _(cls, *args, **kwargs)
obj.attr2 = { }
return obj
# typical beginners' code, inheriting your classes but forgetting to
# call its superclasses' _ _init_ _ methods
class derived(super1, super2):
def _ _init_ _(self):
self.attr1.append(111)
self.attr3 = ( )
# despite the typical beginner's error, you won't get support calls:
d = derived( )
print d
# emits: derived with attr1:[111], attr2:{ }, attr3:( )
Discussion
One of Python's strengths is that it does very
little magic behind the curtainsclose to
nothing, actually. If you know Python in sufficient depth, you know
that essentially all internal mechanisms are clearly documented and
exposed. This strength, however, means that you yourself must do some
things that other languages do magically, such
as prefixing self. to methods and attributes of
the current object and explicitly calling the _ _init_
_ methods of your superclasses in the _ _init_
_ method of your own class.Unfortunately, Python beginners, particularly if they first learned
from other languages where they're used to such
implicit and magical behavior, can take some time adapting to this
brave new world where, if you want something done, you do it.
Eventually, they learn. Until they have learned, at times it seems
that their favorite pastime is filling my mailbox with help requests,
in tones ranging from the humble to the arrogant and angry,
complaining that "my classes don't
work." Almost invariably, this complaint means
they're inheriting from my classes, which are meant
to ease such tasks as displaying GUIs and communicating on the
Internet, and they have forgotten to call my
classes' _ _init_ _ methods from
the _ _init_ _ methods of subclasses they have
coded.To deal with this annoyance, I devised the simple solution shown in
this recipe. Beginners generally don't know about
the _ _new_ _ method, and what they
don't know, they cannot mess up. If they
do know enough to override _ _new_
_, you can hope they also know enough to do a properly
cooperative supercall using the super built-in,
rather than crudely bypassing your code by directly calling
object._ _new_ _. Well, hope springs eternal, or
so they say. Truth be told, my hopes lie in
beginners' total, blissful ignorance about
_ _new_ _and this theory seems to work
because I don't get those kind of help requests any
more. The help requests I now receive seem concerned more with how to
actually use my classes, rather than displaying fundamental ignorance
of Python.If you work with more advanced but equally perverse beginners, ones
quite able to mess up _ _new_ _, you should
consider giving your classes a custom metaclass that, in its
_ _call_ _ (which executes at class instantiation
time), calls a special hidden method on your classes to enable you to
do your initializations anyway. That approach should hold you in good
steadat least until the beginners start learning about
metaclasses. Of course, "it is impossible to make
anything foolproof, because fools are so ingenious"
(Roger Berg). Nevertheless, see Recipe 20.14 for other approaches that
avoid _ _init_ _ for attribute initialization
needs.
See Also
Library Reference and Python in a
Nutshell documentation on special methods _
_init_ _ and _ _new_ _, and built-in
super; Recipe 20.14.