It just occurred to me that my LOAD_GLOBAL/LOAD_ATTR eliding scheme can't work, since LOAD_ATTR calls PyObject_GetAttr, which can wind up calling __getattr__, which is free to inflict all sorts of side effects on the attribute lookup. PEP 267 doesn't appear to be similarly affected, assuming it can conclude that LOAD_GLOBAL is actually loading a module object. (Can it?) LOAD_GLOBAL alone shouldn't be a problem, since all that does is call PyDict_GetItem for globals and builtins.
The approach I'm working on would have to check that the object is a module on each use, but that's relatively cheap compared to the layers of function calls we have now. It's a pretty safe assumption because it would only be made for objects bound by an import statement. I also wanted to answer Samuele's question briefly, because I'm going to be busy with other things most of today. The basic idea, which I need to flesh out by next week, is that the internal binding for "mod.attr" that a module keeps is just a hint. The compiler notices that function f() uses "mod.attr" and that mod is imported at the module level. The "mod.attr" binding must include a pointer to the location where mod is stored and the pointer it found when the "mod.attr" binding was updated. When "mod.attr" is used, the interpreter must check that mod is still bound to the same object. If so, the "mod.attr" binding is still valid. Note that the "mod.attr" binding is a PyObject ** -- a pointer to the location where "attr" is bound in "mod". Jeremy