74 lines
2.0 KiB
Python
74 lines
2.0 KiB
Python
|
from itertools import filterfalse
|
||
|
|
||
|
|
||
|
def unique_everseen(iterable, key=None):
|
||
|
"List unique elements, preserving order. Remember all elements ever seen."
|
||
|
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
|
||
|
# unique_everseen('ABBCcAD', str.lower) --> A B C D
|
||
|
seen = set()
|
||
|
seen_add = seen.add
|
||
|
if key is None:
|
||
|
for element in filterfalse(seen.__contains__, iterable):
|
||
|
seen_add(element)
|
||
|
yield element
|
||
|
else:
|
||
|
for element in iterable:
|
||
|
k = key(element)
|
||
|
if k not in seen:
|
||
|
seen_add(k)
|
||
|
yield element
|
||
|
|
||
|
|
||
|
# copied from more_itertools 8.8
|
||
|
def always_iterable(obj, base_type=(str, bytes)):
|
||
|
"""If *obj* is iterable, return an iterator over its items::
|
||
|
|
||
|
>>> obj = (1, 2, 3)
|
||
|
>>> list(always_iterable(obj))
|
||
|
[1, 2, 3]
|
||
|
|
||
|
If *obj* is not iterable, return a one-item iterable containing *obj*::
|
||
|
|
||
|
>>> obj = 1
|
||
|
>>> list(always_iterable(obj))
|
||
|
[1]
|
||
|
|
||
|
If *obj* is ``None``, return an empty iterable:
|
||
|
|
||
|
>>> obj = None
|
||
|
>>> list(always_iterable(None))
|
||
|
[]
|
||
|
|
||
|
By default, binary and text strings are not considered iterable::
|
||
|
|
||
|
>>> obj = 'foo'
|
||
|
>>> list(always_iterable(obj))
|
||
|
['foo']
|
||
|
|
||
|
If *base_type* is set, objects for which ``isinstance(obj, base_type)``
|
||
|
returns ``True`` won't be considered iterable.
|
||
|
|
||
|
>>> obj = {'a': 1}
|
||
|
>>> list(always_iterable(obj)) # Iterate over the dict's keys
|
||
|
['a']
|
||
|
>>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit
|
||
|
[{'a': 1}]
|
||
|
|
||
|
Set *base_type* to ``None`` to avoid any special handling and treat objects
|
||
|
Python considers iterable as iterable:
|
||
|
|
||
|
>>> obj = 'foo'
|
||
|
>>> list(always_iterable(obj, base_type=None))
|
||
|
['f', 'o', 'o']
|
||
|
"""
|
||
|
if obj is None:
|
||
|
return iter(())
|
||
|
|
||
|
if (base_type is not None) and isinstance(obj, base_type):
|
||
|
return iter((obj,))
|
||
|
|
||
|
try:
|
||
|
return iter(obj)
|
||
|
except TypeError:
|
||
|
return iter((obj,))
|