|
|
 |
|
Title: The simple but handy "collector of a bunch of named stuff" class
Submitter: Alex Martelli
(other recipes)
Last Updated: 2001/04/08
Version no: 1.1
Category:
OOP
|
|
15 vote(s)
|
|
|
|
Description:
Often we want to just collect a bunch of stuff together, naming each item of the bunch; a dictionary's OK for that, but a small do-nothing class is even handier, and prettier to use.
Source: Text Source
class Bunch:
def __init__(self, **kwds):
self.__dict__.update(kwds)
point = Bunch(datum=y, squared=y*y, coord=x)
if point.squared > threshold:
point.isok = 1
Discussion:
Dictionaries are fine for collecting a small bunch of stuff, each item with a name; however, when names are constants and to be used just like variables, the dictionary-access syntax ("if bunch['squared'] > threshold", etc) is not maximally clear; it takes VERY little effort to build a little class, as in the 'Bunch' example above, that will both ease the initialization task _and_ provide elegant attribute-access syntax ("if bunch.squared > threshold", etc).
It would not be hard to add __getitem__, __setitem__ and __delitem__ methods to allow attributes to *also* be accessed as bunch['squared'] etc -- they would just have to delegate to self.__dict__, e.g. via the handy functions getitem, setitem, delitem in module operator. This would mimic javascript use, where attributes and items are regularly confused; such unPythonic idioms, however, seem to be completely useless in Python. For occasional access to an attribute whose name is held in a variable (or otherwise runtime-computed), builtin functions getattr, setattr and delattr are perfectly adequate, and definitely preferable to complicating the delightfully-simple Bunch class.
|
|
Add comment
|
|
Number of comments: 12
Even shorter Bunch, Doug Hudgeon, 2001/08/29
class Bunch:
__init__ = lambda self, **kw: setattr(self, '__dict__', kw)
You could also accept *args and put them in a sequence accessible
with [].
When python 2.2 permits the above dynamic __dict__ this could be
be something like:
class Bunch(list):
def __init__(*args, **kw):
self[:] = list(args)
setattr(self, '__dict__', kw)
Add comment
Using a bunch as a dictionary, Not specified Not specified, 2004/02/19
Quite simple extension of the original Bunch, just one extra line:
class BunchDict(dict):
def __init__(self,**kw):
dict.__init__(self,kw)
self.__dict__.update(kw)
This has the added benefit that it can directly be printed and it shows its contents in interactive environments like ipython.
Add comment
One dictionary is better than two, Graham Fawcett, 2004/10/08
How about just replacing self.__dict__ with self, since self is a dict?
class Bunch(dict):
def __init__(self,**kw):
dict.__init__(self,kw)
self.__dict__ = self
Then any deletions, modifications, etc. are accessible via both the dict and attribute interfaces.
Add comment
CAUTION, Yair Chuchem, 2005/12/27
this one leaks memory
just try:
>>> for i in range(10**9):
... a = Bunch()
you need to stay away from circular references
Add comment
CAUTION, Yair Chuchem, 2005/12/27
this one leaks memory
just try:
>>> for i in range(10**9):
... a = Bunch()
you need to stay away from circular references
Add comment
Using a bunch as a dictionary, Not specified Not specified, 2004/02/19
Quite simple extension of the original Bunch, just one extra line:
class BunchDict(dict):
def __init__(self,**kw):
dict.__init__(self,kw)
self.__dict__.update(kw)
This has the added benefit that it can directly be printed and it shows its contents in interactive environments like ipython.
Add comment
Using dict functionality, Magnus Lie Hetland, 2005/09/01
You could simply do something like this, given the current functionality of dict (with kwd args etc.):
class bunch(dict):
__getattr__ = dict.__getitem__
Of course, you won't get the right exceptions etcl., and simply setting __setattr__ the same way won't really work. But it seems to be about as simple as this recipe can get :)
Add comment
Improvement to handle pickling, Ben North, 2005/11/02
As written, pickling and unpickling such an object breaks the
equivalence between self and self.__dict__. Adding the methods
__getstate__ and __setstate__ used by the pickle protocol fixes this:
class Bunch(dict):
def __init__(self, **kw):
dict.__init__(self, kw)
self.__dict__ = self
def __getstate__(self):
return self
def __setstate__(self, state):
self.update(state)
self.__dict__ = self
(Perhaps there's a better way to do this, but it seems to have the
desired effect.)
Add comment
Ugh; follow-up in wrong place, Ben North, 2005/11/02
Apologies; that was supposed to be a comment on
"One dictionary is better than two", Graham Fawcett
Add comment
CAUTION, Yair Chuchem, 2005/12/27
as noted before - this leaks memory!
Add comment
String representation, Simon Brunning, 2004/12/31
For debugging purposes, it's nice to have a meaningful string representation. This works for me:
def __str__(self):
state = ["%s=%r" % (attribute, value)
for (attribute, value)
in self.__dict__.items()]
return '\n'.join(state)
Add comment
Is there an issue with using the type builtin for this ?, Robin Bryce, 2006/04/02
If your use case is accessing **kw using dot notation then how about:
kw=type('bunch',(), kw)
Or, more closely following the 'utility helper' style of the original:
def bunch(**kw):
return type('bunch',(), kw)
B=bunch(apples=1,pears=2)
b=B()
assert B.apples == b.apples and b.apples=1
class Y(B)
bannanas=3
pears=19
y=Y()
assert y.pears == Y.pears and y.pears != b.pears
Add comment
|
|
|
|
|
 |
|