namespaces module (a.k.a. bunch, struct, generic object, etc.) PEP
Nick Coghlan
ncoghlan at iinet.net.au
Sat Feb 12 02:54:54 EST 2005
Steven Bethard wrote:
> Hmmm... This does seem sensible. And sidesteps the issue about
> suggesting that subclasses use Namespace.update instead of
> namespaceinstance.update -- the latter just won't work! (This is a Good
> Thing, IMHO.)
Yeah, I thought so too. It also crystallised for me that the real problem was
that we were trying to use the attribute namespace for something different from
what it is usually used for, and so Python's normal lookup sequence was a
hindrance rather than a help.
I guess Guido knew what he was doing when he provided the __getattribute__ hook ;)
One other point is that dealing correctly with meta-issues like this greatly
boosts the benefits from having this module in the standard library. I would
expect most of the 'roll-your-own' solutions that are out there deal badly with
this issue. "Inherit from namespaces.Namespace" is a simpler instruction than
"figure out how to write an appropriate __getattribute__ method".
>> 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 :)
>
>
> Yeah, I'm going to look into this a bit more too, but let me know if you
> have any more insights in this area.
My gut feel is that we want to get rid of all the decriptor behaviour for normal
names, but keep it for special names. After all, if people actually wanted that
machinery for normal attributes, they'd be using a normal class rather than a
namespace.
I'm also interested in this because I'd like to let the 'Record' class (see
below) be able to store anything in the defaults, including descriptors and have
things 'just work'.
The __getattribute__ I posted comes close to handling that, but we need
__setattr__ and __delattr__ as well in order to deal fully with the issue, since
override descriptors like property() can affect those operations. Fortunately,
they should be relatively trivial:
# Module helper function
def _isspecial(name):
return name.startswith("__") and name.endswith("__")
# And in Namespace
def __getattribute__(self, name):
"""Namespaces only use __dict__ and __getattr__
for non-magic attribute names.
Class attributes and descriptors like property() are ignored
"""
getattribute = super(Namespace, self).__getattribute__
try:
return getattribute("__dict__")[name]
except KeyError:
if _isspecial(name)
# 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('%s instance has no attribute %s'
% (type(self).__name__, name))
def __setattr__(self, name, val):
"""Namespaces only use __dict__ for non-magic attribute names.
Descriptors like property() are ignored"""
if _isspecial(name):
super(Namespace, self).__setattr__(name, val)
else:
self.__dict__[name] = val
def __delattr__(self, name):
"""Namespaces only use __dict__ for non-magic attribute names.
Descriptors like property() are ignored"""
if _isspecial(name):
super(Namespace, self).__delattr__(name)
else:
del self.__dict__[name]
In action:
Py> def get(self): print "Getting"
...
Py> def set(self, val): print "Setting"
...
Py> def delete(self): print "Deleting"
...
Py> prop = property(get, set, delete)
Py> class C(object):
... x = prop
...
Py> c = C()
Py> c.x
Getting
Py> c.x = 1
Setting
Py> del c.x
Deleting
Py> class NS(namespaces.Namespace):
... x = prop
...
Py> ns = NS()
Py> ns.x
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "namespaces.py", line 40, in __getattribute__
raise AttributeError('%s instance has no attribute %s'
AttributeError: NS instance has no attribute x
Py> ns.x = 1
Py> del ns.x
Py> ns.__x__
Getting
Py> ns.__x__ = 1
Setting
Py> del ns.__x__
Deleting
I've attached my latest local version of namespaces.py (based on the recent PEP
draft) which includes all of the above. Some other highlights are:
NamespaceView supports Namespace instances in the constructor
NamespaceChain inherits from NamespaceView (as per my last message about that)
LockedView is a new class that supports 'locking' a namespace, so you can only
modify existing names, and cannot add or remove them
NamespaceChain and LockedView are the main reason I modified NamespaceView to
directly support namespaces - so that subclassed could easily support using either.
Record inherits from LockedView and is designed to make it easy to define and
create fully specified "data container" objects.
Cheers,
Nick.
--
Nick Coghlan | ncoghlan at email.com | Brisbane, Australia
---------------------------------------------------------------
http://boredomandlaziness.skystorm.net
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: namespaces.py
URL: <http://mail.python.org/pipermail/python-list/attachments/20050212/63060d2f/attachment.ksh>
More information about the Python-list
mailing list