Passed
Push — master ( e7fb2b...77297d )
by sosei
01:00
created

multivalued_dict.multivalued_dict.keys()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
from collections import defaultdict
2
from collections import UserDict
3
from collections.abc import Iterable
4
from collections.abc import Hashable
5
from itertools import filterfalse
6
7
8
class MultivaluedDictError(Exception):
9
    pass
10
11
class KeyValuePairsError(MultivaluedDictError):
12
    def __init__(self, list_of_not_kv_pair):
13
        self.list_of_not_kv_pair = list_of_not_kv_pair
14
15
    def __repr__(self):
16
        return f'{list_of_not_kv_pair} these items do not form key-value pairs. '
17
18
class multivalued_dict(UserDict):
19
    START = 'S'
20
    END = 'E'
21
22
    @classmethod
23
    def __is_multivalued_dict__(cls, x):
24
        return (isinstance(x, cls) or ((True if x.default_factory == type([]) else False) if isinstance(x, defaultdict) else False))
25
26
    def __init__(self, *args, **kwargs):
27
        if not multivalued_dict.__is_multivalued_dict__(self):
28
            raise TypeError(f"descriptor '__init__' requires a 'multivalued_dict' object but received a {type(self)}")
29
        len_of_args = len(args)
30
        if len_of_args > 1:
31
            raise TypeError(f'expected at most 1 arguments, got {len_of_args}')
32
        self.data = defaultdict(list)
33
        if len_of_args == 1:
34
            initial_items = args[0]
35
            if multivalued_dict.__is_multivalued_dict__(initial_items):
36
                self.__mvdict_init__(initial_items)
37
            else:
38
                self.update(initial_items)
39
        if kwargs != dict():
40
            self.update(kwargs)
41
42
    def __mvdict_init__(self, multivalued_init_items):
43
        if multivalued_dict.__is_multivalued_dict__(multivalued_init_items):
44
            self.data = multivalued_init_items
45
        else:
46
            raise TypeError(f"{type(multivalued_init_items)}  objects are not multivalued_dict or defaultdict(<class 'list'>) objects. ")
47
48
    def __repr__(self):
49
        return f'multivalued_dict({dict(self.data)})'
50
51
    def __lenvalue__(self, key = None):
52
        if key is None:
53
            return sum(map(len, self.data.values()))
54
        else:
55
            return len(self.data[key])
56
57
    def __matchkv__(self, key, value):
58
        return value in self.data[key]
59
60
    def __delkv__(self, key, value, allkv = True, direction = START):
61
        assert allkv in (True, False), '"allkv" can only be True or False.'
62
        assert direction in (self.START, self.END), '"direction" can only be START or END.'
63
64
        if allkv:
65
            while value in self.data[key]:
66
                self.data[key].remove(value)
67
        else:
68
            if direction == self.START:
69
                self.data[key].remove(value)
70
            elif direction == self.END:
71
                value_len = len(self.data[key])
72
                for i in range(value_len):
73
                    if self.data[key][-1 - i] == value:
74
                        self.data[key].__delitem__(-1 - i)
75
                        break
76
77
    def count(self, key, value):
78
        return self.data[key].count(value)
79
80
    def update(self, *args, **kwargs):
81
        if not multivalued_dict.__is_multivalued_dict__(self):
82
            raise TypeError(f"descriptor 'update' requires a 'multivalued_dict' object but received a {type(self)}")
83
        len_of_args = len(args)
84
        if len_of_args > 1:
85
            raise TypeError(f'expected at most 1 arguments, got {len_of_args}')
86
        if len_of_args == 1:
87
            update_items = args[0]
88
            if not isinstance(update_items, Iterable):
89
                raise TypeError(f'{type(update_items)} object is not iterable ')
90
            if isinstance(update_items, dict):
91
                for _key, _value in update_items.items():
92
                    self.data[_key].append(_value)
93
            else:
94
                list_of_not_kv_pair = list(filterfalse(lambda item: len(item) == 2, update_items))  #找出不是两个元素的项,也就是无法构成键值对的项
95
                if list_of_not_kv_pair != []:
96
                    raise KeyValuePairsError(list_of_not_kv_pair)
97
                for _key, _value in update_items:
98
                    self.data[_key].append(_value)
99
        if kwargs != dict():
100
            self.update(kwargs)
101
102
    def reverse(self, key):
103
        self.data[key].reverse()
104
105
    def copy(self):
106
        return multivalued_dict(self.data)
107
108
    def items(self):
109
        return self.data.items()
110
111
    def keys(self):
112
        return self.data.keys()
113
114
    def values(self):
115
        return self.data.values()
116
117
    @classmethod
118
    def fromkeys(cls, iterable, value = None):
119
        dict_var = dict.fromkeys(iterable, value)
120
        return cls(dict_var)
121