Project

General

Profile

1
# Dictionaries
2

    
3
import itertools
4
import UserDict
5

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

    
8
class DictProxy(UserDict.DictMixin):
9
    '''A proxy that forwards all accesses to an inner dict.'''
10
    
11
    def __init__(self, inner=None):
12
        if inner == None: inner = {}
13
        self.inner = inner
14
    
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
    
21
    def __delitem__(self, key): del self.inner[key]
22

    
23
class KeyExistsError(KeyError):
24
    def __init__(self, key):
25
        KeyError.__init__(self, key)
26
        
27
        self.key = key
28

    
29
class OnceOnlyDict(DictProxy):
30
    '''A dict that only allows each key to be assigned once (no overwriting).'''
31
    
32
    def __setitem__(self, key, value):
33
        if key in self.inner: raise KeyExistsError(key)
34
        DictProxy.__setitem__(self, key, value)
35

    
36
class IdCompared:
37
    '''A value that's compared using `is` instead of ==.
38
    Do not use this for strings as they are interned, causing `is` to have the
39
    same meaning as ==.
40
    '''
41
    
42
    def __init__(self, value): self.value = value
43
    
44
    def __eq__(self, other):
45
        return (other != None and other.__class__ == self.__class__
46
            and other.value is self.value)
47
    
48
    def __hash__(self): return id(self.value)
49

    
50
class IdDict(dict):
51
    '''A dict that stores objects by id()'''
52
    
53
    def add(self, *values):
54
        for value in values: self[id(value)] = value
55
        return self
56
    
57
    def add_vars(self, vars_): return self.add(*vars_.values())
58

    
59
class MergeDict:
60
    '''A dict that checks each of several dicts'''
61
    
62
    def __init__(self, *dicts): self.dicts = dicts
63
    
64
    def __getitem__(self, key):
65
        for dict_ in self.dicts:
66
            try: return dict_[key]
67
            except KeyError: pass
68
        raise # reraise last KeyError
69

    
70
class AttrsDictView:
71
    '''A dict view of an object's attributes
72
    @pre If you want __iter__() to work, value must have a __dict__
73
    '''
74
    
75
    def __init__(self, value): self.value = value
76
    
77
    def __iter__(self): return iter(self.value.__dict__)
78
    
79
    def __getitem__(self, key): return getattr(self.value, key)
80

    
81
import util
82

    
83
def make_hashable(value):
84
    if isinstance(value, list): value = tuple(value)
85
    elif isinstance(value, dict): value = util.NamedTuple(**value)
86
    return value
87

    
88
def join(dict0, dict1):
89
    '''Joins dict0 (A->B mapping) to dict1 (B->C mapping) SQL-style.
90
    If a value in dict0 has no key in dict1, uses the value itself.
91
    '''
92
    new_dict = {}
93
    for out, in_ in dict0.iteritems():
94
        try: out_value = dict1.get(in_, in_) # use in_ if no match found
95
        except TypeError: out_value = in_ # unhashable type
96
        new_dict[out] = out_value
97
    return new_dict
(14-14/44)