generic object implementation

S

Steven Bethard

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__.update(kwds)

def __eq__(self, other):
if not isinstance(other, bunch):
return False
attrs = set(self.__dict__)
if attrs != set(other.__dict__):
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__.items()))

def update(self, other):
self.__dict__.update(other.__dict__)

@classmethod
def frommapping(cls, mapping,
getkeys=iter, getitem=_operator.getitem):
result = bunch()
for key in getkeys(mapping):
value = getitem(mapping, key)
try:
value = bunch.frommapping(value)
except TypeError:
pass
setattr(result, key, value)
return result
 
B

Bengt Richter

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...
UIAM 'bunch' already has a related meaning from c.l.py past (approx the first
three lines of your class), so how about 'gob' ? ;-)
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__.update(kwds)

def __eq__(self, other):
if not isinstance(other, bunch):
return False
attrs = set(self.__dict__)
if attrs != set(other.__dict__):
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__.items()))

def update(self, other):
self.__dict__.update(other.__dict__)

@classmethod
def frommapping(cls, mapping,
getkeys=iter, getitem=_operator.getitem):
result = bunch()
for key in getkeys(mapping):
value = getitem(mapping, key)
try:
value = bunch.frommapping(value)
except TypeError:
pass
setattr(result, key, value)
return result

I dunno, begins to look more like a dict with getattr access substituted for __getitem__
than a primitive generic object...
... def __metaclass__(name, bases, cdict):
... cdict.update(dict.__dict__)
... cdict['__getattr__'] = cdict['__getitem__']
... cdict['__setattr__'] = cdict['__setitem__']
... cdict['__delattr__'] = cdict['__delitem__']
... def raiseunsub(*ignore): raise TypeError, 'unsubscriptable object'
... cdict['__getitem__'] = raiseunsub
... cdict['__setitem__'] = raiseunsub
... cdict['__delitem__'] = raiseunsub
... return type(name, bases, cdict)
...
>>> db = dbunch(x=123)
>>> db {'x': 123}
>>> db.x 123
>>> db['x']
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File said:
>>> db.y = 456
>>> db {'y': 456, 'x': 123}
>>> db.x = 789
>>> db {'y': 456, 'x': 789}
>>> del db.x
>>> db {'y': 456}
>>> db.x = 101112
>>> db.z = 131415
>>> db {'y': 456, 'x': 101112, 'z': 131415}
>>> db.keys() ['y', 'x', 'z']
>>> db.values() [456, 101112, 131415]
>>> db.items() [('y', 456), ('x', 101112), ('z', 131415)]
>>> for k in db: print k,
...
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
 
S

Steven Bethard

Bengt said:
UIAM 'bunch' already has a related meaning from c.l.py past (approx the first
three lines of your class), so how about 'gob' ? ;-)

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.
I dunno, begins to look more like a dict with getattr access substituted for __getitem__
than a primitive generic object...

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)...
... def __metaclass__(name, bases, cdict):
... cdict.update(dict.__dict__)
... cdict['__getattr__'] = cdict['__getitem__']
... cdict['__setattr__'] = cdict['__setitem__']
... cdict['__delattr__'] = cdict['__delitem__']
... def raiseunsub(*ignore): raise TypeError, 'unsubscriptable object'
... cdict['__getitem__'] = raiseunsub
... cdict['__setitem__'] = raiseunsub
... cdict['__delitem__'] = raiseunsub
... return type(name, bases, cdict)
...

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...
It the hope for a lean representation for small numbers
of items?

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
 
P

Peter Otten

Steven said:
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

Me neither. I called my bunch equivalent Struct, but that name seems
ambiguous because of the struct module.
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.

Please, no. (except __iter__(), maybe)
Here's what I have currently:

import operator as _operator

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

def __eq__(self, other):

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.
if not isinstance(other, bunch):
return False
attrs = set(self.__dict__)
if attrs != set(other.__dict__):
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__.items()))

def update(self, other):
self.__dict__.update(other.__dict__)

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(other, bunch):
other = other.__dict__
self.__dict__.update(other)
self.__dict__.update(kw)

i. e. something matching 2.4's dict.update() functionality as closely as
possible (and appropriate).
@classmethod
def frommapping(cls, mapping,
getkeys=iter, getitem=_operator.getitem):
result = bunch()

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

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,108
Latest member
AlbertEste
Top