[Python-Dev] Breaking calls to object.init/new (original) (raw)

Terry Jones terry at jon.es
Thu Mar 22 16:25:56 CET 2007


Following up to myself, with some examples.

I probably haven't done this as cleanly as is possible, but below are a bunch of classes and subclasses that cleanly deal with passing around arguments, even when those args conflict in name, etc., as outlined in my previous mail.

Here's the general class init pattern:

class myclass(A, B, C, ...):
    def __init__(self, **kwargs):
        initargs = kwargs.setdefault('__initargs__', {})  # 1
        Aargs = initargs.setdefault(A, {})                # 2
        Aargs['limit'] = 10                               # 3
        Bargs = initargs.setdefault(B, {})                # 4
        Bargs['limit'] = 'wednesday'                      # 5
        super(myclass, self).__init__(**kwargs)           # 6
        myargs = initargs.get(myclass, {})                # 7
        limit = myargs.get('limit', 7)                    # 8
        if 'huh?' in myargs: raise Exception              # 9

In words, the steps are:

1. Pull the __initargs__ key out of **kwargs, or create it. This gives you
   the top-level dictionary containing your own arguments, if any, and the
   args for your superclass(es), if any.

2. Get the arguments for superclass A, and
3. Add an argument for A.__init__
4 & 5. Do the same for superclass B. We don't alter args for Superclass C.

6. Call super, passing **kwargs along (this contains the original
   __initargs__ that was sent to us, or the one we made in step 1 if
   there was no prior __initargs__.

7. Our arguments, if any, are in initargs too. Get them.
8. Pull out one of our args, or set a default.
9. Check we didn't get any unexpected args, etc.

Some comments:

There's some example code below.

Terry

class err(Exception): pass

class sum(object): def init(self, **kwargs): print "-> in sum" initargs = kwargs.setdefault('initargs', {}) super(sum, self).init(**kwargs) myargs = initargs.get(sum, {}) limit = myargs.get('limit', 5) print "my limit is %d" % limit

class daylimitmaker(object): def init(self, **kwargs): print "-> in daylimitmaker" initargs = kwargs.setdefault('initargs', {}) super(daylimitmaker, self).init(**kwargs) myargs = initargs.get(daylimitmaker, {}) limit = myargs.get('limit', 'sunday (last day of the week)') print "my limit is '%s'" % limit

class lastday(object): def init(self, **kwargs): print "-> in lastday" initargs = kwargs.setdefault('initargs', {}) super(lastday, self).init(**kwargs) myargs = initargs.get(lastday, {}) limit = myargs.get('limit', 'yesterday') print "my limit is '%s'" % limit

class onelimitarg(object): def init(self, **kwargs): print "-> in onelimitarg" initargs = kwargs.setdefault('initargs', {}) super(onelimitarg, self).init(**kwargs) myargs = initargs.get(onelimitarg, {}) if list(myargs.keys()) != ('limit'): raise err, "hey, someone passed me unknown arguments: %s" % myargs limit = myargs.get('limit', 1) print "my limit is %d" % limit

class sub0(sum): '''Subclass sum, and pass it an explicit limit.''' def init(self, **kwargs): print "-> in sub0" initargs = kwargs.setdefault('initargs', {}) sumargs = initargs.setdefault(sum, {}) sumargs['limit'] = 99 super(sub0, self).init(**kwargs)

class sub1(sum, lastday): '''Subclass sum and lastday, passing both an explicit limit.''' def init(self, **kwargs): print "-> in sub1" initargs = kwargs.setdefault('initargs', {}) sumargs = initargs.setdefault(sum, {}) sumargs['limit'] = 4 lastdayargs = initargs.setdefault(lastday, {}) lastdayargs['limit'] = 'friday' super(sub1, self).init(**kwargs) myargs = initargs.get(sub1, {}) limit = myargs.get('limit', 23) # etc...

class sub2(lastday, sum): '''Subclass lastday and sum (opposite order from sub1), passing both an explicit limit.''' def init(self, **kwargs): print "-> in sub2" initargs = kwargs.setdefault('initargs', {}) sumargs = initargs.setdefault(sum, {}) sumargs['limit'] = 10 lastdayargs = initargs.setdefault(lastday, {}) lastdayargs['limit'] = 'wednesday' super(sub2, self).init(**kwargs) myargs = initargs.get(sub2, {}) # etc...

class sub3(lastday, sum): '''Subclass lastday and sum, letting both use their default limit.''' def init(self, **kwargs): print "-> in sub3" initargs = kwargs.setdefault('initargs', {}) super(sub3, self).init(**kwargs) myargs = initargs.get(sub3, {}) # etc...

class sub4(lastday, sum): '''Subclass lastday and sum, let our init take a limit arg and check for ambiguity.''' def init(self, limit=10, **kwargs): '''This is deliberately ugly, but it can be done.''' print "-> in sub4" initargs = kwargs.setdefault('initargs', {}) sumargs = initargs.setdefault(sum, {}) sumargs['limit'] = limit super(sub4, self).init(**kwargs) myargs = initargs.get(sub4, {}) if 'limit' in myargs: raise err, "hey, someone passed me an initargs limit argument, but i like to get an explicit limit arg.\n"

class sub5(sub3, sub4): def init(self, **kwargs): print "-> in sub5" initargs = kwargs.setdefault('initargs', {}) super(sub5, self).init(**kwargs) myargs = initargs.get(sub5, {}) # etc...

class sub6(sum, daylimitmaker): def init(self, **kwargs): print "-> in sub6" initargs = kwargs.setdefault('initargs', {}) super(sub6, self).init(**kwargs) myargs = initargs.get(sub6, {}) # etc...

class sub7(onelimitarg): def init(self, **kwargs): print "-> in sub7" initargs = kwargs.setdefault('initargs', {}) onelimitargs = initargs.setdefault(onelimitarg, {}) onelimitargs['limit'] = 10 onelimitargs['dummy'] = 'dummy' # Pass an unknown arg. super(sub7, self).init(**kwargs)

if name == 'main': for klass in (sub0, sub1, sub2, sub3, sub4, sub5, sub6, sub7): print "Instantiating %s: mro = %s" % (klass.name, [k.name for k in klass.mro]) try: klass() except err, e: print e print

print "Instantiating sub4(limit=22)"
sub4(limit=22) # Call with explicit arg, the way sub4 likes it.
print

try:
    print "Instantiating sub4 with __initargs__ limit."
     # (The way sub4 doesn't like it.)
    sub4(__initargs__={sub4 : {'limit' : None}})
except err, e:
    print e


More information about the Python-Dev mailing list