Steven said:
It was because these seem like two separate cases that I wanted two
different functions for them (__init__ and, say, dictview)...
The other issue is that a namespace *is* a mutable object, so the default
behaviour should be to make a copy (yeah, I know, I'm contradicting myself - I
only just thought of this issue). So an alternate constructor is definitely the
way to go.
I think Michael's implementation also fell into a trap whereby 'E' couldn't be
used as an attribute name. The version below tries to avoid this (using
magic-style naming for the other args in the methods which accept keyword
dictionaries).
To limit the special casing in update, I've switched to only using __dict__ for
the specific case of instances of namespace (otherwise the semantics are too
hard to explain). This is to allow easy copying of an existing namespace - for
anything else, invoking vars() is easy enough.
And I was reading Carlos's page on MetaTemplate, so I threw in an extra class
"record" which inherits from namespace and allows complex data structures to be
defined via class syntax (see the record.__init__ docstring for details). That
bit's entirely optional, but I thought it was neat.
Finally, I've just used normal names for the functions. I think the issue of
function shadowing is best handled by recommending that all of the functions be
called using the class explicitly - this works just as well for instance methods
as it does for class or static methods.
Cheers,
Nick.
+++++++++++++++++++++++++++++++++++++++++++++
from types import ClassType
class namespace(object):
"""
namespace([namespace|dict]) => object
namespace objects provide attribute access to their __dict__
Complement of vars: vars(object) => object.__dict__
Non-magic methods should generally be invoked via the class to
avoid inadvertent shadowing by instance attributes
Using attribute names that look like magic attributes is not
prohibited but can lead to surprising behaviour.
In general, operations on namespace equate to the operations
on namespace.__dict__
"""
def __init__(__self__, __orig__ = None, **__kwds__):
"""__init__([namespace|dict], **kwds) -> None"""
type(__self__).update(__self__, __orig__, **__kwds__)
@classmethod
def view(cls, orig):
"""namespace.view(dict) -> namespace
Creates a namespace that is a view of the original
dictionary. Allows modification of an existing
dictionary via namespace syntax"""
new = cls()
new.__dict__ = orig
return new
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, repr(self.__dict__))
# Recommend calling non-magic methods via class form to
# avoid problems with inadvertent attribute shadowing
def _checked_update(self, other):
try:
self.__dict__.update(other)
except (TypeError):
raise TypeError("Namespace update requires mapping "
"keyed with valid Python identifiers")
def update(__self__, __other__ = None, **__kwds__):
"""type(self).update(self, [namespace|dict], **kwds) -> None
equivalent to self.__dict__.update"""
# Handle direct updates
if __other__ is not None:
if isinstance(__other__, namespace):
type(__self__)._checked_update(__self__, __other__.__dict__)
else:
type(__self__)._checked_update(__self__, __other__)
# Handle keyword updates
if __kwds__ is not None:
type(__self__)._checked_update(__self__, __kwds__)
class record(namespace):
def __init__(self, definition=None):
"""record([definition]) -> record
Constructs a namespace based on the given class definition
Nested classes are created as sub-records
Fields starting with an underscore are ignored
If definition is not given, uses current class
This is handy with subclasses
Using subclasses this way has the advantage that the
created records are also instances of the subclass.
For example:
Py> from ns import namespace, record
Py> class Record:
... a = 1
... b = ""
... class SubRecord:
... c = 3
...
Py> x = record(Record)
Py> x
record({'a': 1, 'b': '', 'SubRecord': record({'c': 3})})
Py> class Record2(record):
... a = 1
... b = ""
... class SubRecord2(record):
... c =3
...
Py> x = Record2()
Py> x
Record2({'a': 1, 'b': '', 'SubRecord2': SubRecord2({'c': 3})})
"""
cls = type(self)
if definition is None:
definition = cls
cls.update_from_definition(self, definition)
def update_from_definition(self, definition):
"""type(self).update_from_definition(self, definition) -> None
Updates the namespace based on the given class definition
Nested classes are created as sub-records
Fields starting with an underscore are ignored"""
try:
for field, default in definition.__dict__.iteritems():
if field.startswith("_"):
continue
if (isinstance(default, (type, ClassType))
and issubclass(default, record)):
# It's a record, so make it an instance of itself
self.__dict__[field] = default()
else:
try:
# If we can make a record of it, do so
self.__dict__[field] = record(default)
except TypeError:
# Throw it in a standard field
self.__dict__[field] = default
except AttributeError:
raise TypeError("Namespace definition must have __dict__ attribute")