28
28
NamedTuple ,
29
29
NoReturn ,
30
30
MutableMapping ,
31
+ Collection ,
31
32
cast ,
32
33
overload ,
33
34
TYPE_CHECKING
@@ -199,9 +200,16 @@ def __call__(self, *args, **keywords):
199
200
return self .func (* args , * self .args , ** keywords )
200
201
201
202
202
- def split_dict (dct : MutableMapping [KT , VT ], keep_keys : Iterable [KT ],
203
- keep_order : bool = True ) -> Dict [KT , VT ]:
204
- """Pop all items from **dct** which are not in **keep_keys** and use them to construct a new dictionary.
203
+ @overload
204
+ def split_dict (dct : MutableMapping [KT , VT ], preserve_order : bool = ..., * ,
205
+ keep_keys : Iterable [KT ]) -> Dict [KT , VT ]:
206
+ ...
207
+ @overload # noqa: E302
208
+ def split_dict (dct : MutableMapping [KT , VT ], preserve_order : bool = ..., * ,
209
+ disgard_keys : Iterable [KT ]) -> Dict [KT , VT ]:
210
+ ...
211
+ def split_dict (dct : MutableMapping [KT , VT ], preserve_order : bool = False , * , keep_keys : Iterable [KT ] = None , disgard_keys : Iterable [KT ] = None ) -> Dict [KT , VT ]: # noqa: E302,E501
212
+ r"""Pop all items from **dct** which are in not in **keep_keys** and use them to construct a new dictionary.
205
213
206
214
Note that, by popping its keys, the passed **dct** will also be modified inplace.
207
215
@@ -214,48 +222,78 @@ def split_dict(dct: MutableMapping[KT, VT], keep_keys: Iterable[KT],
214
222
>>> dict1 = {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
215
223
>>> dict2 = split_dict(dict1, keep_keys={1, 2})
216
224
217
- >>> print(dict1)
225
+ >>> print(dict1, dict2, sep='\n' )
218
226
{1: 'a', 2: 'b'}
219
-
220
- >>> print(dict2)
221
227
{3: 'c', 4: 'd'}
222
228
229
+ >>> dict3 = split_dict(dict1, disgard_keys={1, 2})
230
+ >>> print(dict1, dict3, sep='\n')
231
+ {}
232
+ {1: 'a', 2: 'b'}
233
+
223
234
Parameters
224
235
----------
225
236
dct : :class:`MutableMapping[KT, VT]<typing.MutableMapping>`
226
237
A mutable mapping.
227
- keep_keys : :class:`Iterable[KT]<typing.Iterable>`
228
- An iterable with keys that should remain in **dct**.
229
- keep_order : :class:`bool`
238
+ preserve_order : :class:`bool`
230
239
If :data:`True`, preserve the order of the items in **dct**.
231
- Note that :code:`keep_order = False` is generally faster.
240
+ Note that :code:`preserve_order = False` is generally faster.
241
+ keep_keys : :class:`Iterable[KT]<typing.Iterable>`, keyword-only
242
+ An iterable with keys that should remain in **dct**.
243
+ Note that **keep_keys** and **disgard_keys** are mutually exclusive.
244
+ disgard_keys : :class:`Iterable[KT]<typing.Iterable>`, keyword-only
245
+ An iterable with keys that should be removed from **dct**.
246
+ Note that **disgard_keys** and **keep_keys** are mutually exclusive.
232
247
233
248
Returns
234
249
-------
235
250
:class:`Dict[KT, VT]<typing.Dict>`
236
251
A new dictionaries with all key/value pairs from **dct** not specified in **keep_keys**.
237
252
238
253
""" # noqa: E501
239
- # The ordering of dict elements is preserved in this manner,
240
- # as opposed to the use of set.difference()
241
- if keep_order :
242
- difference : Iterable [KT ] = [k for k in dct if k not in keep_keys ]
254
+ if keep_keys is disgard_keys is None :
255
+ raise TypeError ("'keep_keys' and 'disgard_keys' cannot both be unspecified" )
256
+ elif keep_keys is not None :
257
+ iterable = _keep_keys (dct , keep_keys , preserve_order )
258
+ elif disgard_keys is not None :
259
+ iterable = _disgard_keys (dct , disgard_keys , preserve_order )
260
+ else :
261
+ raise TypeError ("'keep_keys' and 'disgard_keys' cannot both be specified" )
262
+
263
+ return {k : dct .pop (k ) for k in iterable }
264
+
265
+
266
+ def _keep_keys (dct : Mapping [KT , VT ], keep_keys : Iterable [KT ],
267
+ preserve_order : bool = False ) -> Collection [KT ]:
268
+ """A helper function for :func:`split_dict`; used when :code:`keep_keys is not None`."""
269
+ if preserve_order :
270
+ return [k for k in dct if k not in keep_keys ]
243
271
else :
244
272
try :
245
- difference = dct .keys () - keep_keys # type: ignore
273
+ return dct .keys () - keep_keys # type: ignore
246
274
except (TypeError , AttributeError ):
247
- difference = set (dct .keys ()).difference (keep_keys )
275
+ return set (dct .keys ()).difference (keep_keys )
276
+
248
277
249
- return {k : dct .pop (k ) for k in difference }
278
+ def _disgard_keys (dct : Mapping [KT , VT ], keep_keys : Iterable [KT ],
279
+ preserve_order : bool = False ) -> Collection [KT ]:
280
+ """A helper function for :func:`split_dict`; used when :code:`disgard_keys is not None`."""
281
+ if preserve_order :
282
+ return [k for k in dct if k in keep_keys ]
283
+ else :
284
+ try :
285
+ return dct .keys () + keep_keys # type: ignore
286
+ except TypeError :
287
+ return set (dct .keys ()).union (keep_keys )
250
288
251
289
252
290
@overload
253
- def raise_if (ex : None ) -> Callable [[FT ], FT ]:
291
+ def raise_if (exception : None ) -> Callable [[FT ], FT ]:
254
292
...
255
293
@overload # noqa: E302
256
- def raise_if (ex : BaseException ) -> Callable [[Callable ], Callable [..., NoReturn ]]:
294
+ def raise_if (exception : BaseException ) -> Callable [[Callable ], Callable [..., NoReturn ]]:
257
295
...
258
- def raise_if (ex : Optional [BaseException ]) -> Callable : # noqa: E302
296
+ def raise_if (exception : Optional [BaseException ]) -> Callable : # noqa: E302
259
297
"""A decorator which raises the passed exception whenever calling the decorated function.
260
298
261
299
Examples
@@ -286,24 +324,24 @@ def raise_if(ex: Optional[BaseException]) -> Callable: # noqa: E302
286
324
287
325
Parameters
288
326
----------
289
- ex : :exc:`BaseException`, optional
327
+ exception : :exc:`BaseException`, optional
290
328
An exception.
291
329
If :data:`None` is passed then the decorated function will be called as usual.
292
330
293
331
"""
294
- if ex is None :
332
+ if exception is None :
295
333
def decorator (func : FT ):
296
334
return func
297
335
298
- elif isinstance (ex , BaseException ):
336
+ elif isinstance (exception , BaseException ):
299
337
def decorator (func : FT ):
300
338
@wraps (func )
301
339
def wrapper (* args , ** kwargs ):
302
- raise ex
340
+ raise exception
303
341
return wrapper
304
342
305
343
else :
306
- raise TypeError (f"{ ex .__class__ .__name__ !r} " )
344
+ raise TypeError (f"{ exception .__class__ .__name__ !r} " )
307
345
return decorator
308
346
309
347
0 commit comments