Conflicting needs for __init__ method

fumanchu fumanchu at amor.org
Mon Jan 15 19:02:03 EST 2007


Steven D'Aprano wrote:
> > class Rational(object):
> >     def __init__(self, numerator, denominator):
> >         print "lots of heavy processing here..."
> >         # processing ints, floats, strings, special case arguments,
> >         # blah blah blah...
> >         self.numerator = numerator
> >         self.denominator = denominator
> >     def __copy__(self):
> >         cls = self.__class__
> >         obj = cls.__new__(cls)
> >         obj.numerator = self.numerator
> >         obj.denominator = self.denominator
> >         return obj
> >     def __neg__(self):
> >         obj = self.__copy__()
> >         obj.numerator *= -1
> >         return obj
>
>
> Here's a variation on that which is perhaps better suited for objects with
> lots of attributes:
>
>     def __copy__(self):
>         cls = self.__class__
>         obj = cls.__new__(cls)
>         obj.__dict__.update(self.__dict__) # copy everything quickly
>         return obj

I recently had to do something similar for my ORM, where a
user-instantiated object gets expensive default values, but the back
end just overwrites those defaults when "resurrecting" objects, so it
shouldn't pay the price. However (and this is the tricky part), I also
wanted to allow subclasses to extend the __init__ method, so just using
cls.__new__(cls) didn't quite go far enough. Here's what I ended up
with [1]:

    def __init__(self, **kwargs):
        self.sandbox = None

        cls = self.__class__
        if self._zombie:
            # This is pretty tricky, and deserves some detailed
explanation.
            # When normal code creates an instance of this class, then
the
            # expensive setting of defaults below is performed
automatically.
            # However, when a DB recalls a Unit, we have its entire
properties
            # dict already and should skip defaults in the interest of
speed.
            # Therefore, a DB which recalls a Unit can write:
            #     unit = UnitSubClass.__new__(UnitSubClass)
            #     unit._zombie = True
            #     unit.__init__()
            #     unit._properties = {...}
            # instead of:
            #     unit = UnitSubClass()
            #     unit._properties = {...}
            # If done this way, the caller must make CERTAIN that all
of
            # the values in _properties are set, and must call
cleanse().
            self._properties = dict.fromkeys(cls.properties, None)
        else:
            # Copy the class properties into self._properties,
            # setting each value to the UnitProperty.default.
            self._properties = dict([(k, getattr(cls, k).default)
                                     for k in cls.properties])

            # Make sure we cleanse before assigning properties from
kwargs,
            # or the new unit won't get saved if there are no further
changes.
            self.cleanse()

        for k, v in kwargs.iteritems():
            setattr(self, k, v)

The _zombie argument is therefore a flag which allows you to keep the
initialization code inside __init__ (rather than repeating it inside
every method).


Robert Brewer
System Architect
Amor Ministries
fumanchu at amor.org

[1] http://projects.amor.org/dejavu/browser/trunk/units.py#l552




More information about the Python-list mailing list