empty classes as c structs?

Nick Coghlan ncoghlan at iinet.net.au
Mon Feb 7 07:34:54 EST 2005


Steven Bethard wrote:
> 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")

-- 
Nick Coghlan   |   ncoghlan at email.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://boredomandlaziness.skystorm.net



More information about the Python-list mailing list