codekingpro/portable-devtools
114k
1from itertools import filterfalse2 3 4def unique_everseen(iterable, key=None):5 "List unique elements, preserving order. Remember all elements ever seen."6 # unique_everseen('AAAABBBCCDAABBB') --> A B C D7 # unique_everseen('ABBCcAD', str.lower) --> A B C D8 seen = set()9 seen_add = seen.add10 if key is None:11 for element in filterfalse(seen.__contains__, iterable):12 seen_add(element)13 yield element14 else:15 for element in iterable:16 k = key(element)17 if k not in seen:18 seen_add(k)19 yield element20 21 22# copied from more_itertools 8.823def always_iterable(obj, base_type=(str, bytes)):24 """If *obj* is iterable, return an iterator over its items::25 26 >>> obj = (1, 2, 3)27 >>> list(always_iterable(obj))28 [1, 2, 3]29 30 If *obj* is not iterable, return a one-item iterable containing *obj*::31 32 >>> obj = 133 >>> list(always_iterable(obj))34 [1]35 36 If *obj* is ``None``, return an empty iterable:37 38 >>> obj = None39 >>> list(always_iterable(None))40 []41 42 By default, binary and text strings are not considered iterable::43 44 >>> obj = 'foo'45 >>> list(always_iterable(obj))46 ['foo']47 48 If *base_type* is set, objects for which ``isinstance(obj, base_type)``49 returns ``True`` won't be considered iterable.50 51 >>> obj = {'a': 1}52 >>> list(always_iterable(obj)) # Iterate over the dict's keys53 ['a']54 >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit55 [{'a': 1}]56 57 Set *base_type* to ``None`` to avoid any special handling and treat objects58 Python considers iterable as iterable:59 60 >>> obj = 'foo'61 >>> list(always_iterable(obj, base_type=None))62 ['f', 'o', 'o']63 """64 if obj is None:65 return iter(())66 67 if (base_type is not None) and isinstance(obj, base_type):68 return iter((obj,))69 70 try:71 return iter(obj)72 except TypeError:73 return iter((obj,))74 