Question about accessing class-attributes.

Alex Martelli aleax at aleax.it
Thu Apr 24 16:58:36 EDT 2003


Bjorn Pettersen wrote:
   ...
> Then define a hierarchy:
> 
>  class Counted(object):
> __metaclass__ = metaIC
> 
>  class Point(object):
> __metaclass__ = metaMetaBunch
> x = 0
> y = 0
> 
>  class CountedPoint(Counted, Point):
>      def __init__(self):
>          super(CountedPoint.self).__init__(self)

hmmm. I think you want a comma there, not a dot, in the super args.
And you shouldn't pass self as an argument here, either.  Just:

           super(CountedPoint, self).__init__()

But really, this whole __init__ method has no reason for being,
as it's just delegating to exactly the same method that would be
used if it didn't exist!!!  So, snip it...

   ...
> not exactly clear _what_ the conflict is, i.e. it's not that the meta
> classes must have a common base (both isinstance(x, type), it's not that
> type.__new__ should perhaps be super(x, type).__new__, it _is_ that the
> metaclasses aren't related by inheritance. Trying to fix this by, e.g.

"Related by inheritance" may be a bit too weak.  More specifically:
when class A(B, C, D): ... the constraint is that type(A) is a 
subclass of each of type(B) and type(C).  E.g., try this:

class meta1(type): pass
class C1: __metaclass__ = meta1

class meta2(type): pass
class C2: __metaclass__ = meta2

try:
    print 'C1, C2',
    class C3(C1, C2): pass
except TypeError:
    print 'no'
else:
    print 'yes'

class meta3(meta2, meta1): pass
class C3(C1, C2): __metaclass__ = meta3
print "with metaclass MI, though, it's fine!"


But in some cases, yes, weaker inheritance constraints between
meta-classes can suffice.



> making metaIC a subclass of metaMetaBunch (and changing the __new__ call
> appropriately), gives you:
> 
> Traceback (most recent call last):
>   File "M:\python\cpoint.py", line 38, in ?
>     class CountedPoint(Counted, Point):
>   File "M:\python\cpoint.py", line 25, in __new__
>     return metaMetaBunch.__new__(cls, name, bases, dict)
>   File "M:\python\cpoint.py", line 20, in __new__
>     return type.__new__(cls, classname, bases, newdict)
> TypeError: multiple bases have instance lay-out conflict
> 
> which means you're stuck (?)

Well no -- you MIGHT be stuck if you ever needed two different AND
incompatible __new__ in the same class, but here metaIC may just as
well use __init__ instead (as it ain't messing with e.g. __slots__:-)
so everything works if you recode the whole mess as, e.g.:


class metaMetaBunch(type):
    def __new__(cls, classname, bases, classdict):
        def __init__(self, **kw):
            for k in self.__dflts__: setattr(self, k, self.__dflts__[k])
            for k in kw: setattr(self, k, kw[k])

        newdict = {'__slots__':[], '__dflts__':{}, '__init__':__init__}

        for k in classdict:
            if k.startswith('__'):
                newdict[k] = classdict[k]
            else:
                newdict['__slots__'].append(k)
                newdict['__dflts__'][k] = classdict[k]

        return type.__new__(cls, classname, bases, newdict)

class metaIC(metaMetaBunch):
    def __init__(cls, name, bases, dict):
        cls.InstCount = 0
    def __call__(cls, *args, **kwds):
        cls.InstCount += 1
        return type.__call__(cls, *args, **kwds)

class Counted(object):
        __metaclass__ = metaIC
        
class Point(object):
        __metaclass__ = metaMetaBunch
        x = 0
        y = 0
        
class CountedPoint(Counted, Point): pass
               
cp = CountedPoint()
print cp        


But given the dependencies I don't think this particular mixin
mesh makes much sense.  I'd rather merge two metaclasses into a
third one (solving any __new__-related conflicts of course;-)
rather than arbitrarily deriving one from the other (and it IS
arbitrary -- now you're forcing ANYTHING that's counted to be
SOME kind of metabunch...).


Alex





More information about the Python-list mailing list