multivalued_dict_package.multivalued_dict_module   C
last analyzed

Complexity

Total Complexity 56

Size/Duplication

Total Lines 574
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 56
eloc 126
dl 0
loc 574
rs 5.5199
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A multivalued_dict.fromkeys() 0 13 1
B multivalued_dict.__init__() 0 43 8
A multivalued_dict.__len__() 0 10 1
A multivalued_dict.__repr__() 0 6 1
A multivalued_dict.__iter__() 0 13 1
A multivalued_dict.__is_multivalued_dict__() 0 9 3
A multivalued_dict.keys() 0 14 1
A multivalued_dict.pop() 0 25 2
B multivalued_dict.__delkv__() 0 34 7
A multivalued_dict.__matchkv__() 0 12 1
A multivalued_dict.__lenvalue__() 0 13 2
A multivalued_dict.items() 0 14 1
A multivalued_dict.__setitem__() 0 18 1
A multivalued_dict.values() 0 14 1
A multivalued_dict.popitem() 0 12 1
A multivalued_dict.__reverse__() 0 12 2
A multivalued_dict.__eq__() 0 14 1
A multivalued_dict.count() 0 8 1
A multivalued_dict.__getitem__() 0 17 2
A multivalued_dict.__delitem__() 0 11 1
A multivalued_dict.clear() 0 13 1
A multivalued_dict.setdefault() 0 18 1
D multivalued_dict.update() 0 72 12
A multivalued_dict.get() 0 12 1
A multivalued_dict.copy() 0 18 1
A multivalued_dict.__contains__() 0 14 1

How to fix   Complexity   

Complexity

Complex classes like multivalued_dict_package.multivalued_dict_module 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
'''
2
multivalued_dict - This is a multi-valued dictionary package.
3
Copyright (C) 2019-2020  sosei
4
5
This program is free software: you can redistribute it and/or modify
6
it under the terms of the GNU Affero General Public License as published
7
by the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
GNU Affero General Public License for more details.
14
15
You should have received a copy of the GNU Affero General Public License
16
along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
'''
18
19
from check_self_class_call_of_meta_package import check_self_class_call_of_meta
20
from abc import ABCMeta
21
from collections import UserDict
22
23
__all__ = ['multivalued_dict', 'START_POS', 'END_POS']
24
25
START_POS = 'S'
26
END_POS = 'E'
27
28
class _eliminate_metaclass_conflicts(check_self_class_call_of_meta, ABCMeta):
29
    pass
30
31
class multivalued_dict(UserDict, metaclass = _eliminate_metaclass_conflicts):  #lgtm [py/missing-call-to-init]
32
    '''
33
        multivalued_dict() -> new empty dictionary
34
        multivalued_dict(mapping) -> new dictionary initialized from a mapping object's
35
            (key, value) pairs
36
        multivalued_dict(iterable) -> new dictionary initialized as if via:
37
            d = {}
38
            for k, v in iterable:
39
                d[k].append(v)
40
        multivalued_dict(**kwargs) -> new dictionary initialized with the name=value pairs
41
            in the keyword argument list.  For example:  dict(one=1, two=2)
42
        
43
        >>> mv_d = multivalued_dict()
44
        >>> mv_d
45
        multivalued_dict({})
46
        
47
        >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
48
        >>> mv_d
49
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
50
        
51
        >>> mv_d = multivalued_dict({'a': ['test-1', 'test-2', 'test-3'], 'b': 'test-4'})
52
        >>> mv_d
53
        multivalued_dict({'a': ['test-1', 'test-2', 'test-3'], 'b': ['test-4']})
54
        
55
        >>> mv_d = multivalued_dict([['a', 'test-1'], ['b', 'test-2'], ['a', 'test-3']])
56
        >>> mv_d
57
        multivalued_dict({'a': ['test-1', 'test-3'], 'b': ['test-2']})
58
        
59
        >>> mv_d = multivalued_dict(a = 'test-1', b = 'test-2', c = 'test-3')
60
        >>> mv_d
61
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
62
        
63
        >>> mv_d = multivalued_dict(a = ['test-1', 'test-2', 'test-3'])
64
        >>> mv_d
65
        multivalued_dict({'a': ['test-1', 'test-2', 'test-3']})
66
        
67
        >>> mv_d = multivalued_dict([['a', 'test-1'], ['c', 'test-3']], b = 'test-2')
68
        >>> mv_d
69
        multivalued_dict({'a': ['test-1'], 'c': ['test-3'], 'b': ['test-2']})
70
        
71
        >>> mv_d0 = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
72
        >>> mv_d0
73
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
74
        >>> mv_d = multivalued_dict(mv_d0)
75
        >>> mv_d
76
        multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
77
        
78
        >>> multivalued_dict([['a', 'test-1']], [['b', 'test-2']])
79
        Traceback (most recent call last):
80
        TypeError: multivalued_dict expected at most 1 arguments, got 2
81
    '''
82
    
83
    from collections import defaultdict
84
    from collections.abc import Iterable
85
    
86
    version = '2.0.1'
87
    
88
    __marker = object()
89
    
90
    @classmethod
91
    def __is_multivalued_dict__(cls, x):
92
        '''
93
            >>> mv_d = multivalued_dict()
94
            >>> multivalued_dict.__is_multivalued_dict__(mv_d)
95
            True
96
        '''
97
        
98
        return (isinstance(x, cls) or ((True if x.default_factory == type([]) else False) if isinstance(x, cls.defaultdict) else False))
99
    
100
    @classmethod
101
    def fromkeys(cls, iterable, value = None):
102
        '''
103
            Create a new dictionary with keys from iterable and values set to value.
104
            
105
            >>> multivalued_dict.fromkeys(['a', 'b', 'c'])
106
            multivalued_dict({'a': [None], 'b': [None], 'c': [None]})
107
            >>> multivalued_dict.fromkeys(['a', 'b', 'c'], 'test')
108
            multivalued_dict({'a': ['test'], 'b': ['test'], 'c': ['test']})
109
        '''
110
        
111
        dict_var = dict.fromkeys(iterable, value)
112
        return cls(dict_var)
113
    
114
    def __init__(self, *args, **kwargs):
115
        '''
116
            Initialize self.  See help(type(self)) for accurate signature.
117
            
118
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
119
            >>> mv_d
120
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
121
            
122
            >>> mv_d.__init__({'d': 'test-4'})
123
            >>> mv_d
124
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3'], 'd': ['test-4']})
125
            
126
            >>> multivalued_dict.__init__(mv_d, {'e': 'test-5'})
127
            >>> mv_d
128
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3'], 'd': ['test-4'], 'e': ['test-5']})
129
            
130
            >>> mv_d.__init__({'a': ['test-6', 'test-7']})
131
            >>> mv_d
132
            multivalued_dict({'a': ['test-1', 'test-6', 'test-7'], 'b': ['test-2'], 'c': ['test-3'], 'd': ['test-4'], 'e': ['test-5']})
133
            
134
            >>> multivalued_dict.__init__('x')
135
            Traceback (most recent call last):
136
            TypeError: descriptor '__init__' requires a 'multivalued_dict' object but received a 'str'
137
        '''
138
        
139
        len_of_args = len(args)
140
        if len_of_args > 1:
141
            raise TypeError(f'multivalued_dict expected at most 1 arguments, got {len_of_args}')
142
        else:
143
            if not hasattr(self, 'data'):
144
                self.data = self.defaultdict(list)
145
            if len_of_args == 1:
146
                initial_items = args[0]
147
                if isinstance(initial_items, dict):
148
                    for _key, _value in initial_items.items():
149
                        if isinstance(_value, (tuple, list)):
150
                            self.data[_key].extend(_value)
151
                        else:
152
                            self.data[_key].append(_value)
153
                else:
154
                    self.update(initial_items)
155
        if kwargs != dict():
156
            self.__init__(kwargs)
157
    
158
    def __repr__(self):
159
        '''
160
            Return repr(self).
161
        '''
162
        
163
        return f'multivalued_dict({dict(self.data)})'
164
    
165
    def __iter__(self):
166
        '''
167
            Implement iter(self).
168
            
169
            >>> multivalued_dict(multivalued_dict({'a': 'test-1'}))
170
            multivalued_dict({'a': ['test-1']})
171
            
172
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
173
            >>> multivalued_dict(mv_d.__iter__())
174
            multivalued_dict({'a': [['test-1']], 'b': [['test-2']], 'c': [['test-3']]})
175
        '''
176
        
177
        return iter(self.data.items())
178
    
179
    def __len__(self):
180
        '''
181
            Return len(self).
182
            
183
            >>> mv_d = multivalued_dict([['a', 'test-1'], ['a', 'test-2'], ['a', 'test-3'], ['b', 'test-4']])
184
            >>> mv_d.__len__()
185
            2
186
        '''
187
        
188
        return self.data.__len__()
189
    
190
    def __getitem__(self, key):
191
        '''
192
            x.__getitem__(y) <==> x[y]
193
            
194
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
195
            >>> mv_d['a']
196
            ['test-1']
197
            
198
            >>> mv_d['d']
199
            Traceback (most recent call last):
200
            KeyError: 'd'
201
        '''
202
        
203
        if key in self.data:
204
            return self.data[key]
205
        else:
206
            raise KeyError(key)
207
    
208
    def __eq__(self, other):
209
        '''
210
            Return self==value.
211
            
212
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
213
            >>> mv_d
214
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
215
            >>> mv_d == {'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']}
216
            True
217
            >>> mv_d == {'a': ['test-1'], 'b': ['test-2'], 'c': ['test-0']}
218
            False
219
        '''
220
        
221
        return self.data.__eq__(other)
222
    
223
    def __contains__(self, key):
224
        '''
225
            True if the dictionary has the specified key, else False.
226
            
227
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
228
            >>> mv_d
229
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
230
            >>> 'a' in mv_d
231
            True
232
            >>> 'd' in mv_d
233
            False
234
        '''
235
        
236
        return self.data.__contains__(key)
237
    
238
    def __delitem__(self, key):
239
        '''
240
            Delete self[key].
241
            
242
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
243
            >>> mv_d.__delitem__('b')
244
            >>> mv_d
245
            multivalued_dict({'a': ['test-1'], 'c': ['test-3']})
246
        '''
247
        
248
        self.data.__delitem__(key)
249
    
250
    def __setitem__(self, key, item):
251
        '''
252
            Set self[key] to value.
253
            
254
            >>> mv_d = multivalued_dict([['a', 'test-1'], ['a', 'test-2'], ['a', 'test-3'], ['b', 'test-4']])
255
            >>> mv_d
256
            multivalued_dict({'a': ['test-1', 'test-2', 'test-3'], 'b': ['test-4']})
257
            
258
            >>> mv_d.__setitem__('c', 'test-5')
259
            >>> mv_d
260
            multivalued_dict({'a': ['test-1', 'test-2', 'test-3'], 'b': ['test-4'], 'c': ['test-5']})
261
            
262
            >>> mv_d.__setitem__('a', 'test-0')
263
            >>> mv_d
264
            multivalued_dict({'a': ['test-0'], 'b': ['test-4'], 'c': ['test-5']})
265
        '''
266
        
267
        self.data.__setitem__(key, [item])
268
    
269
    def __lenvalue__(self, key = __marker):
270
        '''
271
            >>> mv_d = multivalued_dict([['a', 1], ['a', 2], ['a', 3], ['b', 1], ['b', 2], ['c', 1]])
272
            >>> mv_d.__lenvalue__()
273
            6
274
            >>> mv_d.__lenvalue__('a')
275
            3
276
        '''
277
        
278
        if key is self.__marker:
279
            return sum(map(len, self.data.values()))
280
        else:
281
            return len(self.data[key])
282
    
283
    def __matchkv__(self, key, value):
284
        '''
285
            >>> mv_d = multivalued_dict([['a', 1], ['a', 2], ['a', 3], ['b', 1], ['b', 2], ['c', 1]])
286
            >>> mv_d.__matchkv__('b', 3)
287
            False
288
            >>> mv_d.__matchkv__('a', 2)
289
            True
290
            >>> mv_d.__matchkv__('d', 1)
291
            False
292
        '''
293
        
294
        return value in self.data[key]
295
    
296
    def __delkv__(self, key, value, allkv = True, direction = START_POS):
297
        '''
298
            >>> mv_d = multivalued_dict([['a', 'x'], ['a', 'y'], ['a', 'z'], ['a', 'y'], ['a', 'z'], ['a', 'y']])
299
            >>> mv_d
300
            multivalued_dict({'a': ['x', 'y', 'z', 'y', 'z', 'y']})
301
            
302
            >>> mv_d.__delkv__('a', 'y', False)
303
            >>> mv_d
304
            multivalued_dict({'a': ['x', 'z', 'y', 'z', 'y']})
305
            
306
            >>> mv_d.__delkv__('a', 'y', False, END_POS)
307
            >>> mv_d
308
            multivalued_dict({'a': ['x', 'z', 'y', 'z']})
309
            
310
            >>> mv_d.__delkv__('a', 'z')
311
            >>> mv_d
312
            multivalued_dict({'a': ['x', 'y']})
313
        '''
314
        
315
        assert allkv in (True, False), '"allkv" can only be True or False'
316
        assert direction in (START_POS, END_POS), '"direction" can only be START_POS or END_POS'
317
        
318
        if allkv:
319
            while value in self.data[key]:
320
                self.data[key].remove(value)
321
        else:
322
            if direction == START_POS:
323
                self.data[key].remove(value)
324
            elif direction == END_POS:
325
                value_len = len(self.data[key])
326
                for i in range(value_len):
327
                    if self.data[key][-1 - i] == value:
328
                        self.data[key].__delitem__(-1 - i)
329
                        break
330
    
331
    def __reverse__(self):
332
        '''
333
            >>> mv_d = multivalued_dict([['a', 1], ['b', 2], ['c', 3]])
334
            >>> mv_d
335
            multivalued_dict({'a': [1], 'b': [2], 'c': [3]})
336
            >>> mv_d.__reverse__()
337
            >>> mv_d
338
            multivalued_dict({'c': [3], 'b': [2], 'a': [1]})
339
        '''
340
        
341
        for _key in reversed(tuple(self.data.keys())):
342
            self.data[_key] = self.data.pop(_key)
343
    
344
    def get(self, key, default = None):
345
        '''
346
            D.get(k[,d]) -> D[k] if k in D, else d.  d defaults to None.
347
            
348
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
349
            >>> mv_d.get('a')
350
            ['test-1']
351
            >>> mv_d.get('d')
352
            [None]
353
        '''
354
        
355
        return self.data.get(key, [default])
356
    
357
    def count(self, key, value):
358
        '''
359
            >>> mv_d = multivalued_dict([['a', 'x'], ['a', 'y'], ['a', 'y'], ['a', 'z'], ['a', 'z'], ['a', 'z']])
360
            >>> mv_d.count('a', 'y')
361
            2
362
        '''
363
        
364
        return self.data[key].count(value)
365
    
366
    def update(self, *args, **kwargs):
367
        '''
368
            >>> mv_d = multivalued_dict()
369
            >>> mv_d
370
            multivalued_dict({})
371
            
372
            >>> mv_d.update({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
373
            >>> mv_d
374
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
375
            
376
            >>> mv_d.update([['a', 'test-4'], ['a', 'test-5']])
377
            >>> mv_d
378
            multivalued_dict({'a': ['test-1', 'test-4', 'test-5'], 'b': ['test-2'], 'c': ['test-3']})
379
            
380
            >>> mv_d.update(c = 'test-3')
381
            >>> mv_d
382
            multivalued_dict({'a': ['test-1', 'test-4', 'test-5'], 'b': ['test-2'], 'c': ['test-3', 'test-3']})
383
            
384
            >>> mv_d.update([['b', 'test-6'], ['c', 'test-7']], a = 'test-8')
385
            >>> mv_d
386
            multivalued_dict({'a': ['test-1', 'test-4', 'test-5', 'test-8'], 'b': ['test-2', 'test-6'], 'c': ['test-3', 'test-3', 'test-7']})
387
            
388
            >>> mv_d.update(multivalued_dict({'d': 'test-9', 'e': 'test-10'}))
389
            >>> mv_d
390
            multivalued_dict({'a': ['test-1', 'test-4', 'test-5', 'test-8'], 'b': ['test-2', 'test-6'], 'c': ['test-3', 'test-3', 'test-7'], 'd': ['test-9'], 'e': ['test-10']})
391
392
            >>> mv_d.update([['a', 'test-1']], [['b', 'test-2']])
393
            Traceback (most recent call last):
394
            TypeError: multivalued_dict expected at most 1 arguments, got 2
395
396
            >>> mv_d.update(1)
397
            Traceback (most recent call last):
398
            TypeError: 'int' object is not iterable
399
            
400
            >>> mv_d.update([1])
401
            Traceback (most recent call last):
402
            TypeError: cannot convert dictionary update sequence element #0 to a sequence
403
            
404
            >>> mv_d.update([['1', '2', '3']])
405
            Traceback (most recent call last):
406
            ValueError: dictionary update sequence element #0 has length 3; 2 is required
407
            
408
            >>> multivalued_dict.update('x')
409
            Traceback (most recent call last):
410
            TypeError: descriptor 'update' requires a 'multivalued_dict' object but received a 'str'
411
        '''
412
        
413
        len_of_args = len(args)
414
        if len_of_args > 1:
415
            raise TypeError(f'multivalued_dict expected at most 1 arguments, got {len_of_args}')
416
        if len_of_args == 1:
417
            update_items = args[0]
418
            if not isinstance(update_items, self.Iterable):
419
                raise TypeError(f"'{update_items.__class__.__name__}' object is not iterable")
420
            if multivalued_dict.__is_multivalued_dict__(update_items):
421
                for _key, _value in update_items.items():
422
                    self.data[_key].extend(_value)
423
            elif isinstance(update_items, dict):
424
                for _key, _value in update_items.items():
425
                    self.data[_key].append(_value)
426
            else:
427
                i = 0
428
                for item in update_items:
429
                    if not isinstance(item, self.Iterable):
430
                        raise TypeError(f'cannot convert dictionary update sequence element #{i} to a sequence')
431
                    if len(item) != 2:
432
                        raise ValueError(f'dictionary update sequence element #{i} has length {len(item)}; 2 is required')
433
                    _key, _value = item
434
                    self.data[_key].append(_value)
435
                    i += 1
436
        if kwargs != dict():
437
            self.update(kwargs)
438
    
439
    def setdefault(self, key, default = None):
440
        '''
441
            D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
442
            
443
            >>> mv_d = multivalued_dict({'a': 'test-1', 'c': 'test-3'})
444
            >>> mv_d.setdefault('a')
445
            ['test-1']
446
            >>> mv_d.setdefault('b')
447
            [None]
448
            >>> mv_d
449
            multivalued_dict({'a': ['test-1'], 'c': ['test-3'], 'b': [None]})
450
            >>> mv_d.setdefault('d', 'test=4')
451
            ['test=4']
452
            >>> mv_d
453
            multivalued_dict({'a': ['test-1'], 'c': ['test-3'], 'b': [None], 'd': ['test=4']})
454
        '''
455
        
456
        return self.data.setdefault(key, [default])
457
    
458
    def pop(self, key, default=__marker):
459
        '''
460
            D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
461
            If key is not found, d is returned if given, otherwise KeyError is raised.
462
            
463
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
464
            >>> mv_d.pop('b')
465
            ['test-2']
466
            >>> mv_d
467
            multivalued_dict({'a': ['test-1'], 'c': ['test-3']})
468
            
469
            >>> mv_d.pop('d')
470
            Traceback (most recent call last):
471
            KeyError: 'd'
472
            
473
            >>> mv_d.pop('d', 'test-0')
474
            ['test-0']
475
            >>> mv_d
476
            multivalued_dict({'a': ['test-1'], 'c': ['test-3']})
477
        '''
478
        
479
        if default is self.__marker:
480
            return self.data.pop(key)
481
        else:
482
            return self.data.pop(key, [default])
483
    
484
    def popitem(self):
485
        '''
486
            D.popitem() -> (k, v), remove and return some (key, value) pair as a 2-tuple; but raise KeyError if D is empty.
487
            
488
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
489
            >>> mv_d
490
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
491
            >>> mv_d.popitem()
492
            ('c', ['test-3'])
493
        '''
494
        
495
        return self.data.popitem()
496
    
497
    def copy(self):
498
        '''
499
            D.copy() -> a shallow copy of D
500
            
501
            >>> mv_d_a = multivalued_dict([['a', 1], ['a', 2], ['a', 3]])
502
            >>> mv_d_b = mv_d_a.copy()
503
            >>> mv_d_a
504
            multivalued_dict({'a': [1, 2, 3]})
505
            >>> mv_d_b
506
            multivalued_dict({'a': [1, 2, 3]})
507
            >>> mv_d_a['a'][1] = 99
508
            >>> mv_d_a
509
            multivalued_dict({'a': [1, 99, 3]})
510
            >>> mv_d_b
511
            multivalued_dict({'a': [1, 2, 3]})
512
        '''
513
        
514
        return multivalued_dict(self.data)
515
    
516
    def items(self):
517
        '''
518
            D.items() -> a set-like object providing a view on D's items
519
            
520
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
521
            >>> for k, v in mv_d.items():
522
            ...     print(f'key = {k}, value = {v}')
523
            ...
524
            key = a, value = ['test-1']
525
            key = b, value = ['test-2']
526
            key = c, value = ['test-3']
527
        '''
528
        
529
        return self.data.items()
530
    
531
    def keys(self):
532
        '''
533
            D.keys() -> a set-like object providing a view on D's keys
534
            
535
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
536
            >>> for k in mv_d.keys():
537
            ...     print(f'key = {k}')
538
            ...
539
            key = a
540
            key = b
541
            key = c
542
        '''
543
        
544
        return self.data.keys()
545
    
546
    def values(self):
547
        '''
548
            D.values() -> an object providing a view on D's values
549
            
550
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
551
            >>> for v in mv_d.values():
552
            ...     print(f'value = {v}')
553
            ...
554
            value = ['test-1']
555
            value = ['test-2']
556
            value = ['test-3']
557
        '''
558
        
559
        return self.data.values()
560
    
561
    def clear(self):
562
        '''
563
            D.clear() -> None.  Remove all items from D.
564
            
565
            >>> mv_d = multivalued_dict({'a': 'test-1', 'b': 'test-2', 'c': 'test-3'})
566
            >>> mv_d
567
            multivalued_dict({'a': ['test-1'], 'b': ['test-2'], 'c': ['test-3']})
568
            >>> mv_d.clear()
569
            >>> mv_d
570
            multivalued_dict({})
571
        '''
572
        
573
        self.data.clear()
574