Recipe6.4.Chaining Dictionary Lookups


Recipe 6.4. Chaining Dictionary Lookups

Credit: Raymond Hettinger

Problem

You have several mappings (usually dicts) and want to look things up in them in a chained way (try the first one; if the key is not there, then try the second one; and so on). Specifically, you want to make a single mapping object that "virtually merges" several others, by looking things up in them in a specified priority order, so that you can conveniently pass that one object around.

Solution

A mapping is a generalized, abstract version of a dictionary: a mapping provides an interface that's similar to a dictionary's, but it may use very different implementations. All dictionaries are mappings, but not vice versa. Here, you need to implement a mapping which sequentially tries delegating lookups to other mappings. A class is the right way to encapsulate this functionality:

class Chainmap(object):     def _ _init_ _(self, *mappings):         # record the sequence of mappings into which we must look         self._mappings = mappings     def _ _getitem_ _(self, key):         # try looking up into each mapping in sequence         for mapping in self._mappings:             try:                 return mapping[key]             except KeyError:                 pass         # `key' not found in any mapping, so raise KeyError exception         raise KeyError, key     def get(self, key, default=None):         # return self[key] if present, otherwise `default'         try:             return self[key]         except KeyError:             return default     def _ _contains_ _(self, key):         # return True if `key' is present in self, otherwise False         try:             self[key]             return True         except KeyError:             return False

For example, you can now implement the same sequence of lookups that Python normally uses for any name: look among locals, then (if not found there) among globals, lastly (if not found yet) among built-ins:

import _ _builtin_ _ pylookup = Chainmap(locals( ), globals( ), vars(_ _builtin_ _))

Discussion

Chainmap relies on minimal functionality from the mappings it wraps: each of those underlying mappings must allow indexing (i.e., supply a special method _ _getitem_ _), and it must raise the standard exception KeyError when indexed with a key that the mapping does not know about. A Chainmap instance provides the same behavior, plus the handy get method covered in Recipe 4.9 and special method _ _contains_ _ (which conveniently lets you check whether some key k is present in a Chainmap instance c by just coding if k in c).

Besides the obvious and sensible limitation of being "read-only", this Chainmap class has othersessentially, it is not a "full mapping" even within the read-only design choice. You can make any partial mapping into a "full mapping" by inheriting from class DictMixin (in standard library module UserDict) and supplying a few key methods (DictMixin implements the others). Here is how you could make a full (read-only) mapping from ChainMap and UserDict.DictMixin:

import UserDict from sets import Set class FullChainmap(Chainmap, UserDict.DictMixin):     def copy(self):         return self._ _class_ _(self._mappings)     def _ _iter_ _(self):         seen = Set( )         for mapping in self._mappings:             for key in mapping:                 if key not in seen:                     yield key                     seen.add(key)     iterkeys = _ _iter_ _     def keys(self):         return list(self)

This class FullChainmap adds one requirement to the mappings it holds, besides the requirements posed by Chainmap: the mappings must be iterable. Also note that the implementation in Chainmap of methods get and _ _contains_ _ is redundant (although innocuous) once we subclass DictMixin, since DictMixin also implements those two methods (as well as many others) in terms of lower-level methods, just like Chainmap does. See Recipe 5.14 for more details about DictMixin.

See Also

Recipe 4.9; Recipe 5.14; the Library Reference and Python in a Nutshell sections on mapping types.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net