[Python-Dev] Classes and Metaclasses in Smalltalk (original) (raw)

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 instancegetattr2() and classlookup().

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/)