Project

General

Profile

1
# Dictionaries
2

    
3
import itertools
4
import UserDict
5

    
6
def is_dict(value): return hasattr(value, 'keys')
7

    
8
def pair_keys(pair_list): return [k for k, v in pair_list]
9

    
10
def pair_values(pair_list): return [v for k, v in pair_list]
11

    
12
class DictProxy(UserDict.DictMixin):
13
    '''A proxy that forwards all accesses to an inner dict.'''
14
    
15
    def __init__(self, inner=None):
16
        if inner == None: inner = {}
17
        self.inner = inner
18
    
19
    def keys(self): return self.inner.keys()
20
    
21
    def __getitem__(self, key): return self.inner[key]
22
    
23
    def __setitem__(self, key, value): self.inner[key] = value
24
    
25
    def __delitem__(self, key): del self.inner[key]
26

    
27
class WrapDict(DictProxy):
28
    '''A dict that runs a function on each value retrieved.'''
29
    
30
    def __init__(self, wrap_func, inner=None):
31
        DictProxy.__init__(self, inner)
32
        
33
        self.wrap_func = wrap_func
34
    
35
    def __getitem__(self, key):
36
        return self.wrap_func(DictProxy.__getitem__(self, key))
37

    
38
class KeyExistsError(KeyError):
39
    def __init__(self, key):
40
        KeyError.__init__(self, key)
41
        
42
        self.key = key
43

    
44
class OnceOnlyDict(DictProxy):
45
    '''A dict that only allows each key to be assigned once (no overwriting).'''
46
    
47
    def __setitem__(self, key, value):
48
        if key in self.inner: raise KeyExistsError(key)
49
        DictProxy.__setitem__(self, key, value)
50

    
51
class IdCompared:
52
    '''A value that's compared using `is` instead of ==.
53
    Do not use this for strings as they are interned, causing `is` to have the
54
    same meaning as ==.
55
    '''
56
    
57
    def __init__(self, value): self.value = value
58
    
59
    def __eq__(self, other):
60
        return (other != None and other.__class__ == self.__class__
61
            and other.value is self.value)
62
    
63
    def __hash__(self): return id(self.value)
64

    
65
class IdDict(dict):
66
    '''A dict that stores objects by id()'''
67
    
68
    def add(self, *values):
69
        for value in values: self[id(value)] = value
70
        return self
71
    
72
    def add_vars(self, vars_): return self.add(*vars_.values())
73

    
74
class MergeDict:
75
    '''A dict that checks each of several dicts'''
76
    
77
    def __init__(self, *dicts): self.dicts = dicts
78
    
79
    def __getitem__(self, key):
80
        for dict_ in self.dicts:
81
            try: return dict_[key]
82
            except KeyError: pass
83
        raise # reraise last KeyError
84

    
85
class AttrsDictView:
86
    '''A dict view of an object's attributes
87
    @pre If you want __iter__() to work, value must have a __dict__
88
    '''
89
    
90
    def __init__(self, value): self.value = value
91
    
92
    def __iter__(self): return iter(self.value.__dict__)
93
    
94
    def __getitem__(self, key): return getattr(self.value, key)
95

    
96
import util
97

    
98
def make_hashable(value):
99
    if isinstance(value, list): value = tuple(value)
100
    elif isinstance(value, dict): value = util.NamedTuple(**value)
101
    return value
102

    
103
def join(dict0, dict1):
104
    '''Joins dict0 (A->B mapping) to dict1 (B->C mapping) SQL-style.
105
    If a value in dict0 has no key in dict1, uses the value itself.
106
    '''
107
    new_dict = {}
108
    for out, in_ in dict0.iteritems():
109
        try: out_value = dict1.get(in_, in_) # use in_ if no match found
110
        except TypeError: out_value = in_ # unhashable type
111
        new_dict[out] = out_value
112
    return new_dict
(15-15/49)