namespaces module (a.k.a. bunch, struct, generic object, etc.) PEP
Nick Coghlan
ncoghlan at iinet.net.au
Fri Feb 11 23:36:51 EST 2005
Steven Bethard wrote:
>>> 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 | ncoghlan at email.com | Brisbane, Australia
---------------------------------------------------------------
http://boredomandlaziness.skystorm.net
More information about the Python-list
mailing list