empty classes as c structs?

Michael Spencer mahs at telcopartners.com
Mon Feb 7 13:10:08 EST 2005


Nick Coghlan wrote:
> Steven Bethard wrote:
> 
>> It was because these seem like two separate cases that I wanted two 
>> different functions for them (__init__ and, say, dictview)...
I see this, but I think it weakens the case for a single implementation, given 
that each implementation is essentially one method.
> 
> The other issue is that a namespace *is* a mutable object, so the 
> default behaviour should be to make a copy
> 
I don't follow this argument.  Why does mutability demand copy?  Given that 
somedict here is either a throwaway (in the classic bunch application ) or a 
dict that must be updated (the wrap-dict case), copying doesn't make much sense 
to me.

OTOH, dict.__init__(dict) copies.  hmmmm....


> 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).

You're right - I hadn't considered that.  In case it wasn't obvious, I was 
matching the argspec of dict.  Your solution avoids the problem.
> 
> To limit the special casing in update, I've switched to only using 
> __dict__ for the specific case of instances of namespace

That seems a pity to me.
  (otherwise the
> semantics are too hard to explain). This is to allow easy copying of an 
> existing namespace - 

Can't this be spelled namespace(somenamespace).__copy__()?

 > for anything else, invoking vars() is easy enough.

If there is potential for confusion, I'd be tempted to disallow namespaces as an 
argument to update/__update__

We could use __add__, instead for combining namespaces

> 
> 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.

Good idea.  The implementation ought to be tested against several plausible 
specializations.
> 
> 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.

I don't like the sound of that.  The whole point here - whether as Steven's nice 
straightforward bunch, as originally conceived, or the other cases you and I and 
others have been 'cluttering' the discussion with ;-)  is convenience, and 
readability.  If there are hoops to jump through to use it, then the benefit is 
quickly reduced to zero.

Regards

Michael

> 
> 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")
> 




More information about the Python-list mailing list