Question about accessing class-attributes.
Bjorn Pettersen
BPettersen at NAREX.com
Thu Apr 24 15:35:02 EDT 2003
> From: Alex Martelli [mailto:aleax at aleax.it]
>
[...]
> >
> > class AbstractBaseCounter(object):
> > __InstCount = {}
> > def InstCount(cls):
> > return AbstractBaseCounter.__InstCount[cls]
> > InstCount = classmethod(InstCount)
> >
> > def __init__(self):
> > cls = self.__class__
> > if cls == AbstractBaseCounter:
> > raise TypeError("Attempt to instantiate abstract base
> > class")
> > d = AbstractBaseCounter.__InstCount
> > if cls in d:
> > d[cls] += 1
> > else:
> > d[cls] = 1
> >
> > class A(AbstractBaseCounter):
> > def __init__(self):
> > AbstractBaseCounter.__init__(self)
> >
> > class B(A): pass
> >
> > a1=A()
> > a2=A()
> > b1=B()
> > b2=B()
> > print B.InstCount()
>
> comparing the complication of this one, and the simplicity of
> the custom metaclass I posted and you snipped, PLUS the minor
> risks such as "B overriding __init__ and forgetting to call
> its superclass's"(:-), I think the custom metaclass is
> vindicated;-).
Oh, no argument it's easier. And although the above has problems,
metaclasses have their own set of problems, e.g.:
For reference:
>>> class metaIC(type):
... def __new__(cls, name, bases, dict):
... dict['InstCount'] = 0
... return type.__new__(cls, name, bases, dict)
... def __call__(cls, *args, **kwds):
... cls.InstCount += 1
... return type.__call__(cls, *args, **kwds)
and the (now canonical) metaMetaBunch (munched version [my apologies],
real version here:
http://mail.python.org/pipermail/python-list/2002-July/112007.html):
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)
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)
cp = CountedPoint()
print cp
we get:
Traceback (most recent call last):
File "M:\python\cpoint.py", line 36, in ?
class CountedPoint(Counted, Point):
File "M:\python\cpoint.py", line 6, in __new__
return type.__new__(cls, name, bases, dict)
TypeError: metatype conflict among bases
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.
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 (?)
> > which happens to work...? I've verified that
> > AbstractBaseCounter.__init__ gets called for B(). Is this
> > correct/documented/guaranteed/works with MI/multiple args/old-style
> > classes, etc., etc.?
>
> well, you COULD get dual calls to that __init__ in presence of
> "inheritance diamonds" -- using super may be safer.
True, although for general use, super might have more issues than it's
worth...
-- bjorn
More information about the Python-list
mailing list