[Python-Dev] Classes and Metaclasses in Smalltalk

Guido van Rossum guido@digicool.com
Wed, 02 May 2001 10:03:37 -0500


[me]
> > The best rule I can think of so far is that DictType.__dict__ gives
> > the *true* set of attribute descriptors for dictionary objects, and is
> > thus similar to Smalltalks's class.methodDict that Jim describes
> > below.  DictType.foo is a shortcut that can resolve to either
> > DictType.__dict__['foo'] or to an attribute (maybe a method) of
> > DictType described in TypeType.__dict__['foo'], whichever is defined.
> > If both are defined, I propose the following, clumsy but backwards
> > compatible rule: if DictType.__dict__['foo'] describes a method, it
> > wins.  Otherwise, TypeType.__dict__['foo'] wins.

[MAL]
> I'm not sure I can follow you here: DictType.__repr__ is the
> representation method of the dictionary and not inherited
> from TypeType, so there should be no problem.

The problem is that both a dictionary object (call it d) and its type
(DictType) have a __repr__ method: repr(d) returns "d", and
repr(DictType) returns "<type 'dictionary'>".

Given the analogy with classes, where str(x) invokes x.__str__() and
x.__str__() can also be called directly, it is not unreasonable to
expect that this works in general, so that repr(d) can be spelled as

    d.__repr__()

and repr(DictType) as

    DictType.__repr__()

And, given another analogy with classes, where x.foo() is equivalent
to x.__class__.foo(x), the two forms above should also be equivalent
to

    d.__class__.__repr__(d)

and

    DictType.__class__.__repr__(DictType)

But since d.__class__ is DictType, we now have two conflicting ways to
derive a meaning for DictType.__repr__: the first one going

    repr(DictType) => DictType.__repr__()

and the second one going

    repr(d) => d.__class__.__repr__(d) => DictType.__repr__(d)

The rule quoted above chooses the second meaning, from the very
pragmatic point that once I allow subclassing from DictType, such a
subclass might very well want to override __repr__ to wrap the base
class __repr__, and the conventional way to reference that (barring
the implementation of 'super') is DictType.__repr__.  Direct
invocation of an object's own __repr__ method as x.__repr__() is much
les common.  The implementation of repr(x) can do the right thing,
which is to look for x.__class__.__dict__['__repr__'].

> The problem with the misleading error message would only show
> up in case DictType does not define a __repr__ method. Then the
> inherited one from TypeType would come into play and cause
> the problem you mention above.

No, the issue is not inheritance: I haven't implemented inheritance
yet.  DictType is an instance of TypeType but doesn't inherit from it.

> Thinking in terms of meta-classes, I believe we should implement
> this mechanism in the meta-class (TypeType in this case). Its
> __getattr__() will have to decide whether or not to expose its
> own methods and attributes or not.

That's exactly how I solved it: type_getattro() implements the rule
quoted at the top.

> The only catch here is that currently instances and classes have
> control of whether and how to bind found functions as methods or not.
> We should  probably change that to pass complete control over to the
> meta-class object and remove the special control flows currently found
> in instance_getattr2() and class_lookup().

Um, yeah, that's where I think this will end up causing more trouble.

Right now, if x is an instance, some attributes like x.__class__ and
x.__dict__ special-cased in instance_getattr().  The mechanism I
propose removes the need for (most of) such special cases, and instead
allows the class to provide "descriptors" for instance attributes.
So, for example, if instances of a class C have an attribute named
foo, C.__dict__['foo'] contains the descriptor for that attribute, and
that is how the implementation decides how to interpret x.foo
(assuming x is an instance of C).  We may be able to access this same
descriptor as C.foo, but that's really only important for backwards
compatibility with the way classes work today.

> In general, I think that meta-classes should not expose their
> attributes to the class objects they create, since this causes
> way to many problems.

I agree.

> Perhaps I'm oversimplifying things here, but I have a feeling that
> we can go a long way by actually trying to see meta-classes as
> first class members in the interpreter design and moving all the
> binding and lookup mechanisms over to this object type. The special
> casing should then take place in the meta-class rather than its
> creations.

Yes, that's where I'm heading!

--Guido van Rossum (home page: http://www.python.org/~guido/)