ASPN ActiveState Programmer Network  
ActiveState, a division of Sophos
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups
Submit Recipe
My Recipes

All Recipes
All Cookbooks


View by Category

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

 

4 stars 15 vote(s)


Approved

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)

# that's it!  Now, you can create a Bunch
# whenever you want to group a few variables:

point = Bunch(datum=y, squared=y*y, coord=x)

# and of course you can read/write the named
# attributes you just created, add others, del
# some of them, etc, etc:
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



Highest rated recipes:

1. A simple XML-RPC server

2. Web service accessible ...

3. Treat the Win32 Registry ...

4. Watching a directory ...

5. Union Find data structure

6. Function Decorators by ...

7. MS SQL Server log monitor

8. Table objects with ...

9. wx twisted support using ...

10. More accurate sum




Privacy Policy | Email Opt-out | Feedback | Syndication
© 2006 ActiveState Software Inc. All rights reserved.