Passed
Push — master ( a6429c...4fec5f )
by sosei
01:08
created

multivalued_dict.multivalued_dict.update()   D

Complexity

Conditions 13

Size

Total Lines 28
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 24
nop 2
dl 0
loc 28
rs 4.2
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like multivalued_dict.multivalued_dict.update() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 HashError(MultivaluedDictError):
19
    def __init__(self, list_of_not_hash):
20
        self.list_of_not_hash = list_of_not_hash
21
22
    def __repr__(self):
23
        return f'{list_of_not_hash} these are unhashable. '
24
25
class multivalued_dict(UserDict):
26
    START = 'S'
27
    END = 'E'
28
29
    @classmethod
30
    def __is_multivalued_dict__(cls, x):
31
        return (isinstance(x, cls) or ((True if x.default_factory == type([]) else False) if isinstance(x, defaultdict) else False))
32
33
    def __init__(*args, **kwargs):
34
        if args == ():
35
            raise TypeError("The '__init__' method needs to be instantiated. ")
36
        self, *args = args
37
        len_of_args = len(args)
38
        if len_of_args == 1:
39
            initial_items = args[0]
40
            self.data = defaultdict(list)
41
            if multivalued_dict.__is_multivalued_dict__(initial_items):
42
                self.__mvdict_init__(initial_items)
43
            else:
44
                self.update(initial_items)
45
        elif len_of_args > 1:
46
            raise TypeError(f'expected at most 1 arguments, got {len_of_args}')
47
        if kwargs != dict():
48
            self.update(kwargs)
49
50
    def __mvdict_init__(self, multivalued_init_items):
51
        if multivalued_dict.__is_multivalued_dict__(multivalued_init_items):
52
            self.data = multivalued_init_items
53
        else:
54
            raise TypeError(f"{type(multivalued_init_items)}  objects are not multivalued_dict or defaultdict(<class 'list'>) objects. ")
55
56
    def __repr__(self):
57
        return f'multivalued_dict({dict(self.data)})'
58
59
    def __lenvalue__(self, key = None):
60
        if key is None:
61
            return sum(map(len, self.data.values()))
62
        else:
63
            return len(self.data[key])
64
65
    def __matchkv__(self, key, value):
66
        return value in self.data[key]
67
68
    def __delkv__(self, key, value, allkv = True, direction = START):
69
        assert allkv in (True, False), '"allkv" can only be True or False.'
70
        assert direction in (self.START, self.END), '"direction" can only be START or END.'
71
72
        if allkv:
73
            while value in self.data[key]:
74
                self.data[key].remove(value)
75
        else:
76
            if direction == self.START:
77
                self.data[key].remove(value)
78
            elif direction == self.END:
79
                value_len = len(self.data[key])
80
                for i in range(value_len):
81
                    if self.data[key][-1 - i] == value:
82
                        self.data[key].__delitem__(-1 - i)
83
                        break
84
85
    def count(self, key, value):
86
        return self.data[key].count(value)
87
88
    def update(*args, **kwargs):
89
        if args == ():
90
            raise TypeError("The 'update' method needs to be instantiated. ")
91
        self, *args = args
92
        len_of_args = len(args)
93
        if len_of_args == 1:
94
            update_items = args[0]
95
            if isinstance(update_items, Iterable):
96
                if isinstance(update_items, dict):
97
                    for _key, _value in update_items.items():
98
                        self.data[_key].append(_value)
99
                else:
100
                    list_of_not_kv_pair = list(filterfalse(lambda item: len(item) == 2, update_items))  #找出不是两个元素的项,也就是无法构成键值对的项
101
                    if list_of_not_kv_pair == []:
102
                        list_of_not_hash = list(filterfalse(lambda _key: isinstance(_key, Hashable), (item[0] for item in update_items)))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable item does not seem to be defined.
Loading history...
103
                        if list_of_not_hash == []:  #检测所有键必须可散列
104
                            for _key, _value in update_items:
105
                                self.data[_key].append(_value)
106
                        else:
107
                            raise HashError(list_of_not_hash)
108
                    else:
109
                        raise KeyValuePairsError(list_of_not_kv_pair)
110
            else:
111
                raise TypeError(f'{type(update_items)} object is not iterable. ')
112
        elif len_of_args > 1:
113
            raise TypeError(f'expected at most 1 arguments, got {len_of_args}')
114
        if kwargs != dict():
115
            self.update(kwargs)
116
117
    def reverse(self, key):
118
        self.data[key].reverse()
119
120
    def copy(self):
121
        return multivalued_dict(self.data)
122
123
    def items(self):
124
        return self.data.items()
125
126
    def keys(self):
127
        return self.data.keys()
128
129
    def values(self):
130
        return self.data.values()
131
132
    @classmethod
133
    def fromkeys(cls, iterable, value = None):
134
        dict_var = dict.fromkeys(iterable, value)
135
        return cls(dict_var)
136