1.8 Collecting a Bunch of Named Items


Credit: Alex Martelli

1.8.1 Problem


You want to collect a bunch of items together, naming each item of the bunch, and you find dictionary syntax a bit heavyweight for the purpose.


1.8.2 Solution


Any (classic) class inherently wraps a dictionary, and we take advantage of this:


   1 class Bunch:
   2     def _ _init_ _(self, **kwds):
   3         self._ _dict_ _.update(kwds)

Now, to group a few variables, create a Bunch instance:


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

You can access and rebind the named attributes just created, add others, remove some, and so on. For example:


   1 if point.squared > threshold:
   2     point.isok = 1

1.8.3 Discussion


Often, we just want to collect a bunch of stuff together, naming each item of the bunch; a dictionary's okay for that, but a small do-nothing class is even handier and is prettier to use.


A dictionary is fine for collecting a few items in which each item has a name (the item's key in the dictionary can be thought of as the item's name, in this context). However, when all names are identifiers, to be used just like variables, the dictionary-access syntax is not maximally clear:


   1 if point['squared'] > threshold

It takes minimal effort to build a little class, as in this recipe, to ease the initialization task and provide elegant attribute-access syntax:

花费更小的代价构造一个很小的类,就象在这个配方里, 更容易初始化任务并提供优雅的属性访问语法:

   1 if bunch.squared > threshold

An equally attractive alternative implementation to the one used in the solution is:


   1 class EvenSimplerBunch:
   2     def _ _init_ _(self, **kwds): self._ _dict_ _ = kwds

The alternative presented in the Bunch class has the advantage of not rebinding self._ _dict_ _ (it uses the dictionary's update method to modify it instead), so it will keep working even if, in some hypothetical far-future dialect of Python, this specific dictionary became nonrebindable (as long, of course, as it remains mutable). But this EvenSimplerBunch is indeed even simpler, and marginally speedier, as it just rebinds the dictionary.

在选择的Bunch类里表示有一个好处,不用重新绑定self._ _dict_ _(取而代之的是它使用字典的update方法去更改它)。所以它将继续工作,即使,在一些假定python遥远未来的方言中,这个特殊的字典变得不可绑定时(当然,只要它仍然可变)但是当它仅仅重绑定字典的时候EvenSimplerBunch确实很简单,并且更快。

It is not difficult to add special methods to allow attributes to be accessed as bunch['squared'] and so on. In Python 2.1 or earlier, for example, the simplest way is:


   1 import operator
   2 class MurkierBunch:
   3     def _ _init_ _(self, **kwds):
   4         self._ _dict_ _ = kwds
   5     def _ _getitem_ _(self, key):
   6         return operator.getitem(self._ _dict_ _, key)
   7     def _ _setitem_ _(self, key, value):
   8         return operator.setitem(self._ _dict_ _, key, value)
   9     def _ _delitem_ _(self, key):
  10         return operator.delitem(self._ _dict_ _, key)

In Python 2.2, we can get the same effect by inheriting from the dict built-in type and delegating the other way around:

在Python 2.2,我们能通过继承从字典内建类型得到相同的效果并且委托周围的其他方法:

   1 class MurkierBunch22(dict):
   2     def _ _init_ _(self, **kwds): dict._ _init_ _(self, kwds)
   3     _ _getattr_ _ = dict._ _getitem_ _
   4     _ _setattr_ _ = dict._ _setitem_ _
   5     _ _delattr_ _ = dict._ _delitem_ _

Neither approach makes these Bunch variants into fully fledged dictionaries. There are problems with each梖or example, what is someBunch.keys supposed to mean? Does it refer to the method returning the list of keys, or is it just the same thing as someBunch['keys']? It's definitely better to avoid such confusion: Python distinguishes between attributes and items for clarity and simplicity. However, many newcomers to Python do believe they desire such confusion, generally because of previous experience with JavaScript, in which attributes and items are regularly confused. Such idioms, however, seem to have little usefulness in Python. For occasional access to an attribute whose name is held in a variable (or otherwise runtime-computed), the built-in functions getattr, setattr, and delattr are quite adequate, and they are definitely preferable to complicating the delightfully simple little Bunch class with the semantically murky approaches shown in the previous paragraph.

两种方法都不能使这些Bunch变量制造成完全的字典。每个方法或者例子都有问题, someBunch.keys的假设意味着什么?它引用返回键列表的方法,或者仅仅和someBunch['keys']相同吗?它明显更好的避免那样的混淆:为了清楚和简单,python区分属性和条目。然而,许多python新手相信他们期望那样的混淆,一般是因为以前JavaScript的经验。在JavaScript中,属性和条目被有规律的混淆。然而,那样的习惯用法似乎在python里没有什么用。为了偶然访问一个名字在变量里的属性(或者另外运行时的计算),内建的函数,getattr, setattr, 和delattr是足够了。对于显示在前面带有晦涩语义复杂化优美简单的小Bunch类的方法而言,他们是更优越的。

1.8.4 See Also


The Tutorial section on classes.

PyCkBk-1-8 (last edited 2009-12-25 07:15:35 by localhost)