total idiot question: +=, .=, etc...

Tim Peters tim_one at email.msn.com
Tue Jun 29 09:15:19 CEST 1999


>>     class A(B):
>>         def __init__(self, x, y, z):
>>             B.__init__(self, x, y, z)
>>
>> can be grating too

[Greg Ewing]
> I could live with having to explicitly name the
> superclass, provided something checked that the
> class I named was actually a direct base class
> of the one where the method is defined. That
> way things would be less likely to break
> mysteriously when I rearrange the class
> hierarchy.
>
> I'm still thinking about how to implement
> this...

Hard!  Class attrs (like __init__) don't point back to the class they belong
to.  In the absence of sharing, though, the class chain can be searched for
the class that owns a particular attr value.  I'll attach something that
does that; it's ugly; I'll never use it <wink>; could speed it a lot by
caching the search result.

seems-a-lot-easier-to-do-it-by-eye-ly y'rs  - tim

class BaseInit:
    """A mixin class for "safe" superclass __init__ calls.

    class X(Y, Z, ..., BaseInit):
        def __init__(self, whatever):
            self.baseinit(K, whatever)

    self.baseinit(K, whatever) invokes K.__init__(self, whatever), but
    raises an error unless K is a direct base class of X (the class
    in which the __init__ calling baseinit is defined).
    """

    def baseinit(self, base, *args, **kwargs):
        import sys
        if not isinstance(self, base):
            raise TypeError(`self` + " isn't even an instance of " +
                            `base`)
        # init <- code object of __init__ we were called from
        try:
            raise 'bogus'
        except 'bogus':
            tb = sys.exc_info()[2]
            init = tb.tb_frame.f_back.f_code  # back to the __init__
            if init.co_name != "__init__":
                raise ValueError("baseinit must be called from __init__")
        # search the class hierarchy for that __init__, setting
        # c to the class it belongs to
        candidates = [self.__class__]
        while candidates:
            c = candidates.pop(0)
            if c.__dict__.has_key("__init__"):
                cinit = c.__init__.im_func.func_code
                if init is cinit:
                    break
            candidates = list(c.__bases__) + candidates
        else:
            raise SystemError("didn't find __init__ in class or bases!")
        if base not in c.__bases__:
            raise TypeError("attempt to init superclass " + `base` +
                            " from " + `c` + " but the "
                            "superclass isn't an immediate base class")
        apply(base.__init__, (self,) + args, kwargs)

class A:
    def __init__(self):
        print "in A's init"

class B(A, BaseInit):
    def __init__(self):
        print "in B's init"
        self.baseinit(A)

class C(B, BaseInit):
    def __init__(self):
        print "in C's init"
        self.baseinit(B)
        print "next one should be bogus"
        self.baseinit(A)

b = B()
c = C()






More information about the Python-list mailing list