Question about accessing class-attributes.

Alex Martelli aleax at aleax.it
Tue Apr 29 09:42:16 EDT 2003


Bjorn Pettersen wrote:

>> From: Alex Martelli [mailto:aleax at aleax.it]
>> 
>> Bjorn Pettersen wrote:
>>    ...
>> > Then define a hierarchy:
>> > 
> [...]
>> >  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:
> 
> Sorry about the dot, a little premature copying. That __init__ doesn't
> take self is an interesting artifact of the implementation [super(..)
> seems to want to be object substitutable, but only is some of the
> time...]

It returns an instance, and you call methods on that instance -- just
as you would call methods on other instances, as opposed to calling
them on classes.  What's confusing about it?

> 
> [...]
>> > metaclasses aren't related by inheritance. Trying to fix
>> > this by, e.g.
>> 
>> "Related by inheritance" may be a bit too weak.  More specifically:
> 
> Yes, but not in this case, which is why I didn't want to say:
> 
> [..for all B in C.__bases__, C.__metaclass__ must be subclass of
> B.__metaclass__...]
> 
> Not that I didn't know them, I just haven't figured out why they must be
> so restrictive ? I'm guessing it has to do with attribute lookup rules,
> however I haven't been able to find a good description of attribute
> lookup for new-style classes... (references?) E.g. why is the metaclass'
> __getattr__ never called?

Uh, what do you mean with this last question...?

>>> class meta(type):
...   def __getattr__(self, name):
...     if len(name)==1: return name
...     raise AttributeError, name
...
>>> __metaclass__=meta
>>> class X: pass
...
>>> X.h
'h'
>>>

Of COURSE meta.__getattr__ gets called -- when you get an attribute
from an instance of meta, i.e., a class having meta as its metaclass.

X().h will NOT call meta.__getattr__ -- NO operation on an instance
EVER goes "up" to the metaclass of that instance's class, EVER.  NOT
ONE.  Think about it -- would you WANT all operations invoked on
an instance to go up to the metaclass?  So for example EVERY instance
would be callable -- and use the metaclass's __call__ (like calling
the CLASS of the instance) thus create a new instance totally unrelated
to the calling one (except by having the same type)?  I would find
THAT hypothetical feature extremely confusing, personally.

So maybe I don't understand correctly what you're asking...?


>> > 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.:
> 
> Which is fine as long as you have access to the code of both the Point
> and Counted classes and their metaclasses, understand metaclasses in
> general, the ramifications of the particular implementation and how
> other things would break if you started changing things, and even then
> you're stuck if they both need __new__, unless you can code up a
> compromise. Which was basically my only point :-) While the abstract

I don't get this "only point".  How does it differ from the coupling
from any class and its multiple bases?  I.e., aren't you just as stuck
if multiple bases all need __new__, just as if multiple metaclasses do?

"Understanding metaclasses in general" isn't all that different from
"understanding classes in general", methinks -- as long as you recall
that a class is to a metaclass like an instance is to a class, no more,
no less.  OK, we (collectively) aren't very uses to this, yet, but it's
nothing all that terrible.

> base class solution does have its problems, so does the metaclass
> solution (I wouldn't consider mucking with superclasses' implementation
> just because you need to multiply inherit as beeing in the OO spirit
> <wink>) -- the metaclass solution is aesthetically more pleasing though
> :-)

When you need to multiply inherit and the superclasses were never
designed to coexist (e.g. they BOTH have their own special __new__)
you have issues, of course.  With OR without custom metaclasses.

> Well, _I_ just wanted a countable point, someone else forced me into
> hacking "magic code" just because I wanted to use regular inheritance
> <grin>.

If you only want countable points, it may be silly to use ANY kind
of inheritance or custom metaclasses -- just put everything into the
one class that you say you want.  But if you also want points that
need not be countable, countables that need not be points, and so on,
then refactoring commonalities may be worth it.  Custom metaclasses
are one powerful way to refactor out certain kinds of commonalities
for future reuse (and to avoid code duplication), just like, for some
other kinds, inheritance is.  Not ALL that much difference here -- in
no way is one more "magic" or abstruse than the other, except perhaps
transiently by reasons of familiarity.


Alex





More information about the Python-list mailing list