Project

General

Profile

1 1876 aaronmk
# Dictionaries
2
3 1885 aaronmk
import itertools
4 2651 aaronmk
import UserDict
5 1876 aaronmk
6 2657 aaronmk
def is_dict(value): return hasattr(value, 'keys')
7 2649 aaronmk
8 2651 aaronmk
class DictProxy(UserDict.DictMixin):
9
    '''A proxy that forwards all accesses to an inner dict.'''
10
11 3720 aaronmk
    def __init__(self, inner=None):
12
        if inner == None: inner = {}
13
        self.inner = inner
14 2651 aaronmk
15
    def keys(self): return self.inner.keys()
16
17
    def __getitem__(self, key): return self.inner[key]
18
19
    def __setitem__(self, key, value): self.inner[key] = value
20 6405 aaronmk
21
    def __delitem__(self, key): del self.inner[key]
22 2651 aaronmk
23 6412 aaronmk
class WrapDict(DictProxy):
24
    '''A dict that runs a function on each value retrieved.'''
25
26
    def __init__(self, wrap_func, inner=None):
27
        DictProxy.__init__(self, inner)
28
29
        self.wrap_func = wrap_func
30
31
    def __getitem__(self, key):
32
        return self.wrap_func(DictProxy.__getitem__(self, key))
33
34 3717 aaronmk
class KeyExistsError(KeyError):
35
    def __init__(self, key):
36
        KeyError.__init__(self, key)
37
38
        self.key = key
39
40
class OnceOnlyDict(DictProxy):
41
    '''A dict that only allows each key to be assigned once (no overwriting).'''
42
43
    def __setitem__(self, key, value):
44
        if key in self.inner: raise KeyExistsError(key)
45
        DictProxy.__setitem__(self, key, value)
46
47 2670 aaronmk
class IdCompared:
48
    '''A value that's compared using `is` instead of ==.
49
    Do not use this for strings as they are interned, causing `is` to have the
50
    same meaning as ==.
51
    '''
52
53
    def __init__(self, value): self.value = value
54
55
    def __eq__(self, other):
56
        return (other != None and other.__class__ == self.__class__
57
            and other.value is self.value)
58
59
    def __hash__(self): return id(self.value)
60
61 1885 aaronmk
class IdDict(dict):
62
    '''A dict that stores objects by id()'''
63
64
    def add(self, *values):
65
        for value in values: self[id(value)] = value
66
        return self
67
68
    def add_vars(self, vars_): return self.add(*vars_.values())
69
70 1876 aaronmk
class MergeDict:
71
    '''A dict that checks each of several dicts'''
72 1885 aaronmk
73 1876 aaronmk
    def __init__(self, *dicts): self.dicts = dicts
74
75
    def __getitem__(self, key):
76
        for dict_ in self.dicts:
77
            try: return dict_[key]
78
            except KeyError: pass
79
        raise # reraise last KeyError
80 1907 aaronmk
81 1911 aaronmk
class AttrsDictView:
82
    '''A dict view of an object's attributes
83
    @pre If you want __iter__() to work, value must have a __dict__
84
    '''
85
86
    def __init__(self, value): self.value = value
87
88
    def __iter__(self): return iter(self.value.__dict__)
89
90
    def __getitem__(self, key): return getattr(self.value, key)
91
92 2817 aaronmk
import util
93
94 1907 aaronmk
def make_hashable(value):
95
    if isinstance(value, list): value = tuple(value)
96
    elif isinstance(value, dict): value = util.NamedTuple(**value)
97
    return value
98 2384 aaronmk
99
def join(dict0, dict1):
100
    '''Joins dict0 (A->B mapping) to dict1 (B->C mapping) SQL-style.
101
    If a value in dict0 has no key in dict1, uses the value itself.
102
    '''
103
    new_dict = {}
104
    for out, in_ in dict0.iteritems():
105 6224 aaronmk
        try: out_value = dict1.get(in_, in_) # use in_ if no match found
106
        except TypeError: out_value = in_ # unhashable type
107
        new_dict[out] = out_value
108 2384 aaronmk
    return new_dict