generic object implementation

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Steven Bethard

    #1

    generic object implementation

    So I'm trying to get a prototype implementation of the 'generic object'
    type. (I'm currently calling it 'bunch', but only because I can't
    really think of anything better.) I'd like some feedback on what
    methods it needs to support. Ideally I'd keep this as minimal as
    possible...

    Remember that the goal of the 'generic object' type is to allow the
    programmer to make the design decision that attribute-style access is
    more appropriate than []-style access. Given that, my feeling is that
    the 'generic object' type should *not* support __(get|set|del) item__ ,
    though I'm still undecided on __len__, __iter__, __contains__, items,
    keys, values, etc.

    Here's what I have currently:

    import operator as _operator

    class bunch(object):
    def __init__(self, **kwds):
    self.__dict__.u pdate(kwds)

    def __eq__(self, other):
    if not isinstance(othe r, bunch):
    return False
    attrs = set(self.__dict __)
    if attrs != set(other.__dic t__):
    return False
    for attr in attrs:
    if not getattr(self, attr) == getattr(other, attr):
    return False
    return True

    def __repr__(self):
    return '%s(%s)' % (self.__class__ .__name__,
    ', '.join('%s=%r' % (k, v)
    for k, v in self.__dict__.i tems()))

    def update(self, other):
    self.__dict__.u pdate(other.__d ict__)

    @classmethod
    def frommapping(cls , mapping,
    getkeys=iter, getitem=_operat or.getitem):
    result = bunch()
    for key in getkeys(mapping ):
    value = getitem(mapping , key)
    try:
    value = bunch.frommappi ng(value)
    except TypeError:
    pass
    setattr(result, key, value)
    return result
  • Bengt Richter

    #2
    Re: generic object implementation

    On Sun, 21 Nov 2004 23:55:48 GMT, Steven Bethard <steven.bethard @gmail.com> wrote:
    [color=blue]
    >So I'm trying to get a prototype implementation of the 'generic object'
    >type. (I'm currently calling it 'bunch', but only because I can't
    >really think of anything better.) I'd like some feedback on what
    >methods it needs to support. Ideally I'd keep this as minimal as
    >possible...[/color]
    UIAM 'bunch' already has a related meaning from c.l.py past (approx the first
    three lines of your class), so how about 'gob' ? ;-)
    [color=blue]
    >
    >Remember that the goal of the 'generic object' type is to allow the
    >programmer to make the design decision that attribute-style access is
    >more appropriate than []-style access. Given that, my feeling is that
    >the 'generic object' type should *not* support __(get|set|del) item__ ,
    >though I'm still undecided on __len__, __iter__, __contains__, items,
    >keys, values, etc.
    >
    >Here's what I have currently:
    >
    >import operator as _operator
    >
    >class bunch(object):
    > def __init__(self, **kwds):
    > self.__dict__.u pdate(kwds)
    >
    > def __eq__(self, other):
    > if not isinstance(othe r, bunch):
    > return False
    > attrs = set(self.__dict __)
    > if attrs != set(other.__dic t__):
    > return False
    > for attr in attrs:
    > if not getattr(self, attr) == getattr(other, attr):
    > return False
    > return True
    >
    > def __repr__(self):
    > return '%s(%s)' % (self.__class__ .__name__,
    > ', '.join('%s=%r' % (k, v)
    > for k, v in self.__dict__.i tems()))
    >
    > def update(self, other):
    > self.__dict__.u pdate(other.__d ict__)
    >
    > @classmethod
    > def frommapping(cls , mapping,
    > getkeys=iter, getitem=_operat or.getitem):
    > result = bunch()
    > for key in getkeys(mapping ):
    > value = getitem(mapping , key)
    > try:
    > value = bunch.frommappi ng(value)
    > except TypeError:
    > pass
    > setattr(result, key, value)
    > return result[/color]

    I dunno, begins to look more like a dict with getattr access substituted for __getitem__
    than a primitive generic object...
    [color=blue][color=green][color=darkred]
    >>> class dbunch(dict):[/color][/color][/color]
    ... def __metaclass__(n ame, bases, cdict):
    ... cdict.update(di ct.__dict__)
    ... cdict['__getattr__'] = cdict['__getitem__']
    ... cdict['__setattr__'] = cdict['__setitem__']
    ... cdict['__delattr__'] = cdict['__delitem__']
    ... def raiseunsub(*ign ore): raise TypeError, 'unsubscriptabl e object'
    ... cdict['__getitem__'] = raiseunsub
    ... cdict['__setitem__'] = raiseunsub
    ... cdict['__delitem__'] = raiseunsub
    ... return type(name, bases, cdict)
    ...[color=blue][color=green][color=darkred]
    >>> db = dbunch(x=123)
    >>> db[/color][/color][/color]
    {'x': 123}[color=blue][color=green][color=darkred]
    >>> db.x[/color][/color][/color]
    123[color=blue][color=green][color=darkred]
    >>> db['x'][/color][/color][/color]
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "<stdin>", line 7, in raiseunsub
    TypeError: unsubscriptable object[color=blue][color=green][color=darkred]
    >>> db.y = 456
    >>> db[/color][/color][/color]
    {'y': 456, 'x': 123}[color=blue][color=green][color=darkred]
    >>> db.x = 789
    >>> db[/color][/color][/color]
    {'y': 456, 'x': 789}[color=blue][color=green][color=darkred]
    >>> del db.x
    >>> db[/color][/color][/color]
    {'y': 456}[color=blue][color=green][color=darkred]
    >>> db.x = 101112
    >>> db.z = 131415
    >>> db[/color][/color][/color]
    {'y': 456, 'x': 101112, 'z': 131415}[color=blue][color=green][color=darkred]
    >>> db.keys()[/color][/color][/color]
    ['y', 'x', 'z'][color=blue][color=green][color=darkred]
    >>> db.values()[/color][/color][/color]
    [456, 101112, 131415][color=blue][color=green][color=darkred]
    >>> db.items()[/color][/color][/color]
    [('y', 456), ('x', 101112), ('z', 131415)][color=blue][color=green][color=darkred]
    >>> for k in db: print k,[/color][/color][/color]
    ...
    y x z

    .... and etc. methods from dict, so should it be a restricted builtin subclass of dict
    or derive from object directly? It the hope for a lean representation for small numbers
    of items?

    Regards,
    Bengt Richter

    Comment

    • Steven Bethard

      #3
      Re: generic object implementation

      Bengt Richter wrote:[color=blue]
      >
      > UIAM 'bunch' already has a related meaning from c.l.py past (approx the first
      > three lines of your class), so how about 'gob' ? ;-)[/color]

      Yeah, the intent is basically to provide that 3-liner. Could be that's
      all that should be in the class... It wouldn't cover all my use cases,
      but I'd certainly be happier with that than nothing.
      [color=blue]
      > I dunno, begins to look more like a dict with getattr access substituted for __getitem__
      > than a primitive generic object...[/color]

      Yeah, the idea is to provide getattr access instead of __getitem__ --
      that's basically what the original 3-liner does too. For my uses, I
      don't think I'd ever want any of __len__, __iter__, __contains__, items,
      keys, values, etc. but I wasn't the only one who seemed interested in
      the idea... The pyparsing code in particular might benefit from
      something like the frommapping method (and I know I could use it to
      convert XML DOM into nested objects)...
      [color=blue][color=green][color=darkred]
      > >>> class dbunch(dict):[/color][/color]
      > ... def __metaclass__(n ame, bases, cdict):
      > ... cdict.update(di ct.__dict__)
      > ... cdict['__getattr__'] = cdict['__getitem__']
      > ... cdict['__setattr__'] = cdict['__setitem__']
      > ... cdict['__delattr__'] = cdict['__delitem__']
      > ... def raiseunsub(*ign ore): raise TypeError, 'unsubscriptabl e object'
      > ... cdict['__getitem__'] = raiseunsub
      > ... cdict['__setitem__'] = raiseunsub
      > ... cdict['__delitem__'] = raiseunsub
      > ... return type(name, bases, cdict)
      > ...[/color]

      Interesting approach here. I hadn't thought of going this way. While I
      hadn't planned on supporting the whole mapping interface, this would
      certainly be an easy way to do so. I'll probably wait for a little more
      feedback to see what the others who were interested want to use these
      for... If people have good use cases for keys, items, etc. this is
      probably the right answer...
      [color=blue]
      > It the hope for a lean representation for small numbers
      > of items?[/color]

      Not exactly sure what you mean by lean...

      All the use cases I have for it only care about 'obj.x' type access, and
      don't really use any of the other container type methods. So in my
      case, __init__, __eq__, __repr__, update and frommapping do pretty much
      everything I want... If this is true for everyone else's use cases, I'd
      rather not "pollute" the class with a bunch of dict methods... But I'm
      certainly willing to be persuaded that other methods are also essential.

      Steve

      Comment

      • Peter Otten

        #4
        Re: generic object implementation

        Steven Bethard wrote:
        [color=blue]
        > So I'm trying to get a prototype implementation of the 'generic object'
        > type. (I'm currently calling it 'bunch', but only because I can't
        > really think of anything better.) I'd like some feedback on what[/color]

        Me neither. I called my bunch equivalent Struct, but that name seems
        ambiguous because of the struct module.
        [color=blue]
        > methods it needs to support. Ideally I'd keep this as minimal as
        > possible...
        >
        > Remember that the goal of the 'generic object' type is to allow the
        > programmer to make the design decision that attribute-style access is
        > more appropriate than []-style access. Given that, my feeling is that
        > the 'generic object' type should *not* support __(get|set|del) item__ ,
        > though I'm still undecided on __len__, __iter__, __contains__, items,
        > keys, values, etc.[/color]

        Please, no. (except __iter__(), maybe)
        [color=blue]
        >
        > Here's what I have currently:
        >
        > import operator as _operator
        >
        > class bunch(object):
        > def __init__(self, **kwds):
        > self.__dict__.u pdate(kwds)
        >
        > def __eq__(self, other):[/color]

        Maybe just
        return (type(self) == type(other)
        and self.__dict__ == other.__dict__)

        as you won't get properties right anyway.
        Not sure whether that should be enforced, but subclassing bunch doesn't seem
        a good idea to me.
        [color=blue]
        > if not isinstance(othe r, bunch):
        > return False
        > attrs = set(self.__dict __)
        > if attrs != set(other.__dic t__):
        > return False
        > for attr in attrs:
        > if not getattr(self, attr) == getattr(other, attr):
        > return False
        > return True
        >
        > def __repr__(self):
        > return '%s(%s)' % (self.__class__ .__name__,
        > ', '.join('%s=%r' % (k, v)
        > for k, v in self.__dict__.i tems()))
        >
        > def update(self, other):
        > self.__dict__.u pdate(other.__d ict__)[/color]

        I don't think update() is necessary, but if so, I'd rather have (untested)

        def update(self, *other, **kw):
        if other:
        if len(other) != 1:
        raise TypeError
        other = other[0]
        if isinstance(othe r, bunch):
        other = other.__dict__
        self.__dict__.u pdate(other)
        self.__dict__.u pdate(kw)

        i. e. something matching 2.4's dict.update() functionality as closely as
        possible (and appropriate).
        [color=blue]
        >
        > @classmethod
        > def frommapping(cls , mapping,
        > getkeys=iter, getitem=_operat or.getitem):
        > result = bunch()[/color]

        Substitute bunch with cls throughout.
        Should there also be a getpairs argument that would yield (key, value)
        pairs?
        [color=blue]
        > for key in getkeys(mapping ):
        > value = getitem(mapping , key)
        > try:
        > value = bunch.frommappi ng(value)
        > except TypeError:
        > pass
        > setattr(result, key, value)
        > return result[/color]

        Seeing how many aspects are to consider with such a simple thing as a bunch,
        it would really be a benefit if one standard approach were available to
        substitute all the ad hoc solutions currently out there in the wild.
        Good luck with your effort.

        Peter

        Comment

        Working...