[Types-sig] Meta-stuff
Gordon McMillan
gmcm@hypernet.com
Tue, 8 Dec 1998 15:30:26 -0500
Gordon's Predicated Provisional Proposed MetaClass Pre-Proposal (
Puh-puh-Proposed MC puh-Proposal ), or, How To Punt on the Theory
Stuff:
Observation 1: People use metaclasses to do getattr hooks.
Observation 2: Getattr hooks come in 3, no, 4 flavors:
a) None (Surprise!)
b) Vanilla (magic invoked when attr not found by normal means)
c) Some attrs hidden from normal means, so magic invoked even
though attr is in the normal search path.
d) Complete rewrite of the search mechanism.
Observation 3: Flavors (a) and (b) are normal Python. Flavors (c) and
(d) are what current metaclasses are used for.
Observation 4: Usage of current metaclass hooks causes Spontaneous
Brain Explosions.
Goal: Reduce SBEs by 80%.
Technique: Make the attribute search mechanism a Framework, with the
possibiity to intervene at selected points.
Current mechanism:
1. if name is "__dict__" or "__class__", return those
2. look in instance.__dict__
3. do a class_lookup:
3a check class __dict__
3b for base in bases: do a class_lookup
4 if found:
4a if a function, bind as method
4b if a method, rebind
4c return it
5 if there's a getattr hook, call it, return the result
Now let's distinguish between "get" (the whole search starting from
instance through class and bases to the magic __getattr__), from
"Get" (which is the recursive class_lookup routine), from "GET" which
is the raw look in a dictionary-or-moral-equivalent routine. (The
lower the level, the upper the case.) And then there's
"__GetNonExisting__", or the current __getattr__ hook.
So, very roughly we have:
get(inst, name) is:
found, obj = GET(inst, name)
if not found:
found, obj = Get(inst.__class__, name)
if not found:
hook = get(inst, "__GetNonExisting__")
if hook:
found, obj = apply(hook, ...)
if not found:
raise AttributeError, ...
if isFunction(obj):
obj = bind(...)
return obj
and Get(obj, name) is
found, obj = GET(obj, name)
if not found:
for base in obj.__bases__:
found, obj = Get(base, name)
if found: break
return found, obj
and GET(obj, name) is (usually):
try: attr = obj.__dict__[name]
except: return (0, None)
else: return (1, attr)
Now lets turn that into a Framework.
1) The object has a chance to override GET (his attributes may not
be in a dict name __dict__; indeed, maybe not in a dict at all).
2) The class-like object which is the root of the branch being
searched has a chance to specify a new search pattern (ie,
override "Get").
3) The root class-like object can intervene in any of the above, or
in the way the instance is searched (ie, override "get").
Penultimately, let's use a callback whenever an attribute matching
"name" is found. The callback can say "dandy, we're done", or it can
say "keep looking". In fact, the callback is responsible for whatever
binding, wrapping or perversity that needs to be performed. So what
gets returned to the caller of "get" is actually what the callback
has picked, accumulated or munged. So, conceivably, by overriding the
callback, we could chain together all the base class methods of the
same name (this is a perversity that was asked for on c.l.p. by
someone who shall remain unnamed - but I could give you a hint,
if he doesn't pay me off first...).
This scheme also makes it possible to (relatively) straightforwardly
pull tricks that currently require hiding the real attribute, so the
magic hook will get invoked.
Also, "bind" is part of the framework, so it can be overridden
without horsing around with the other stuff.
Finally, let's provide a default implementation of the
framework that behaves like current Python. Call it the default
Turtle. Let classes (instances, too?) specify their Turtles if they
so desire.
Caveat: I have not explicitly dealt with how to handle multiple
Turtles in the search. I think, with properly executed callbacks and
temporary objects, it's possible to let them wrap each other's
results. Whether the resultant object works as expected is another
question.
Cop-out: I have some REAL work to do; I've hardly touched Just's
stuff or JimF's stuff, so don't hold your breath waiting for a
prototype implementation of the PPPMCPP.
and-th-th-th-that's-all-folks-ly y'rs
- Gordon