empty classes as c structs?

Michael Spencer mahs at telcopartners.com
Sun Feb 6 23:33:39 EST 2005


Alex Martelli wrote:
> Steven Bethard <steven.bethard at gmail.com> wrote:
> 
> 
>>Hmm... interesting.  This isn't the main intended use of 
>>Bunch/Struct/whatever, but it does seem like a useful thing to have...
>>I wonder if it would be worth having, say, a staticmethod of Bunch that
>>produced such a view, e.g.:
>>
>>class Bunch(object):
>>     ...
>>     @staticmethod
>>     def view(data):
>>         result = Bunch()
>>         result.__dict__ = data
>>         return result
>>
>>Then you could write your code as something like:
>>
>>gbls = Bunch.view(globals())
>>
>>I'm probably gonna need more feedback though from people though to know
>>if this is a commonly desired use case...
> 
> 
> Reasonably so, is my guess.  Witness the dict.fromkeys classmethod -- it
> gives you, on dict creation, the same kind of nice syntax sugar that
> wrapping a dict in a bunch gives you for further getting and setting
> (and with similar constraints: all keys must be identifiers and not
> happen to clash with reserved words).
> 
> I think this ``view'' or however you call it should be a classmethod
> too, for the same reason -- let someone handily subclass Bunch and still
> get this creational pattern w/o extra work.  Maybe a good factoring
> could be something like:
> 
> class Bunch(object):
> 
>     def __init__(self, *a, **k):
>         self.__dict__.update(*a, **k)
> 
>     def getDict(self):
>         return self.__dict__
> 
>     def setDict(self, adict):
>         self.__dict__ = adict
> 
>     theDict = property(getDict, setDict, None, 
>                        "direct access to the instance dictionary"
>                       )
> 
>     @classmethod
>     def wrapDict(cls, adict, *a, **k):
>         result = cls.__new__(cls, *a, **k)
>         result.setDict(adict)
>         cls.__init__(result, *a, **k)
>         return result
> 
> I'm thinking of use cases where a subclass of Bunch might override
> setDict (to do something else in addition to Bunch.setDict, e.g.
> maintain some auxiliary data structure for example) -- structuring
> wrapDict as a classmethod in a ``Template Method'' DP might minimize the
> amount of work, and the intrusiveness, needed for the purpose.  (I don't
> have a real-life use case for such a subclass, but it seems to cost but
> little to provide for it as a possibility anyway).
> 
> [[given the way property works, one would need extra indirectness in
> getDict and setDict -- structuring THEM as Template Methods, too -- to
> fully support such a subclass; but that's a well-known general issue
> with property, and the cost of the extra indirection -- mostly in terms
> of complication -- should probably not be borne here, it seems to me]]
> 
> 
> Alex

Steven et al

I like the idea of making the 'bunch' concept a little more standard.
I also like the suggestion Nick Coghlan cited (not sure who suggested the term 
in this context) of calling this 'namespace' in part because it can lead to 
easily-explained semantics.

ISTM that 'bunch' or 'namespace' is in effect the complement of vars i.e., while 
vars(object) => object.__dict__, namespace(somedict) gives an object whose 
__dict__ is somedict.

Looked at this way, namespace (or bunch) is a minimal implementation of an 
object that implements the hasattr(object,__dict__) protocol.  The effect of the 
class is to make operations on __dict__ simpler.  namespace instances can be 
compared with any other object that has a __dict__.  This differs from the PEP 
reference implementation which compares only with other bunch instances.  In 
practice, comparisons with module and class may be useful.

The class interface implements the protocol and little else.

For 'bunch' applications, namespace can be initialized or updated with keyword 
args (just like a dict)
i.e.,
  >>> bunch = namespace({"a":1,"b":2})
can also be written as
  >>> bunch = namespace(a=1,b=2)

For dict-wrapping applications:
  >>> wrappeddict = namespace(bigdict)
but, unlike the PEP implmentation, this sets wrappeddict.__dict__ = bigdict

I think that this interface allows for either use case, without introducing 
'fromdict' classmethod.

Some dict-operations e.g., __copy__ might carry over to the namespace class

Michael


An implementation follows:


# An alternative implementation of Steven Bethard's PEP XXX 'bunch' with
# slightly different interface and semantics:

class namespace(object):
     """
     namespace(somedict) => object (with object.__dict__ = somedict)
     NB, complement of vars:  vars(object) => object.__dict__

     namespace objects provide attribute access to their __dict__

     In general, operations on namespace equate to the operations
     on namespace.__dict__

     """

     def __init__(self, E = None, **F):
         """__init__([namespace|dict], **kwds) -> None"""
         if isinstance(E, dict):
             self.__dict__ = E
         elif hasattr(E, "__dict__"):
             self.__dict__ = E.__dict__
         self.__dict__.update(**F)

     # define only magic methods to limit pollution
     def __update__(self, E = None, **F):
         """update([namespace|dict], **kwds) -> None
         equivalent to self.__dict__.update
         with the addition of namespace as an acceptable operand"""
         if hasattr(other, "keys"):
             self.__dict__.update(E)
         elif hasattr(other, "__dict__"):
             self.__dict__.update(E.__dict__)
         self.__dict__.update(**F)
     def __repr__(self):
         return "namespace(%s)" % repr(self.__dict__)

     # Possible additional methods:  (All are conveniences for dict operations
     # An operation on namespace translates operation on namespace.__dict__
     # So A op B => A.__dict__ op B.__dict__)

     def __copy__(self):
         return namespace(self.__dict__.__copy__)
     def __eq__(self, other):
         return self.__dict__ == other.__dict__

     def __contains__(self, key):
         return self.__dict__.__contains__(key)
     def __iter__(self):
         return iter(self.__dict__)
     # etc...




More information about the Python-list mailing list