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