Should namespace chaining be supported? One suggestion would add a NamespaceChain object to the module::
This does have the advantage of keeping the basic namespace simple. However, it may also be worth having native chaining support in Namespace:
I think I prefer the separate NamespaceChain object because it allows you to chain namespaces other than just Namespace objects -- any object that supports getattr is okay. If chaining is builtin, all namespaces (except the last one?) have to be Namespace objects...
I like NamespaceChain too - I was simply thinking it might be good to have a recursive chaining method associated with actual Namespace objects as well.
However, now that I look more closely at NamespaceChain, it makes more sense to me to explicitly make the 'head' of the chain a namespace view in its own right. This should also make the local binding, chained lookup behaviour fairly obvious (since it will work just as it does for any class with __getattr__ defined, but not __setattr__ or __delattr__).
That is, something like:
class NamespaceChain(NamespaceView): def __init__(self, head, *args): NamespaceView.__init__(self, head) self.__namespaces__ = args
def __getattr__(self, name): """Return the first such attribute found in the object list
This is only invoked for attributes not found in the head namespace. """ for obj in self.__namespaces__: try: return getattr(obj, name) except AttributeError: pass raise AttributeError(name)
Python gives us the local set and local del for free.
The 'nested namespaces' approach that prompted my original suggestion can then be spelt by using:
parent = Namespace() child1 = NamespaceChain({}, parent) child2 = NamespaceChain({}, child1)
There *is* a problem with using __getattr__ though - any attribute in the chained namespaces that is shadowed by a class attribute (like 'update') will be picked up from the class, not from the chained namespaces. So we do need to use __getattribute__ to change that lookup order. However, given the amount of grief the default lookup behaviour causes, I think the place to do that is in Namespace itself:
class Namespace(object): # otherwise unchanged def __getattribute__(self, name): """Namespaces do NOT default to looking in their class dict for non-magic attributes """ getattribute = super(Namespace, self).__getattribute__ try: return getattribute("__dict__")[name] except KeyError: if name.startswith('__') and name.endswith('__'): # Employ the default lookup system for magic names return getattribute(name) else: # Skip the default lookup system for normal names if hasattr(self, "__getattr__"): return getattribute("__getattr__")(name) else: raise AttributeError(name)
The above is a pretty simple approach - it completely bypasses the descriptor machinery for non-magic names. This means that the instance namespace does NOT get polluted by any non-magic names in the class dictionary, but magic names can be accessed normally. And subclasses can add their own methods, and this feature will continue to 'just work'. For example:
Py> ns = namespaces.Namespace() Py> ns.update Traceback (most recent call last): File "<stdin>", line 1, in ? File "namespaces.py", line 30, in __getattribute__ raise AttributeError(name) AttributeError: update Py> type(ns).update <unbound method Namespace.update> Py> ns.__init__ <bound method Namespace.__init__ of Namespace()>
(side note: I don't think I have ever written a __getattribute__ method without introducing an infinite recursion on the first attempt. . .)
Anyway, it is probably worth digging into the descriptor machinery a bit further in order to design a lookup scheme that is most appropriate for namespaces, but the above example has convinced me that object.__getattribute__ is NOT it :)
Cheers, Nick.
-- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --------------------------------------------------------------- http://boredomandlaziness.skystorm.net
-- http://mail.python.org/mailman/listinfo/python-list