|
1 |
| -from itertools import filterfalse |
| 1 | +# from more_itertools 9.0 |
| 2 | +def only(iterable, default=None, too_long=None): |
| 3 | + """If *iterable* has only one item, return it. |
| 4 | + If it has zero items, return *default*. |
| 5 | + If it has more than one item, raise the exception given by *too_long*, |
| 6 | + which is ``ValueError`` by default. |
| 7 | + >>> only([], default='missing') |
| 8 | + 'missing' |
| 9 | + >>> only([1]) |
| 10 | + 1 |
| 11 | + >>> only([1, 2]) # doctest: +IGNORE_EXCEPTION_DETAIL |
| 12 | + Traceback (most recent call last): |
| 13 | + ... |
| 14 | + ValueError: Expected exactly one item in iterable, but got 1, 2, |
| 15 | + and perhaps more.' |
| 16 | + >>> only([1, 2], too_long=TypeError) # doctest: +IGNORE_EXCEPTION_DETAIL |
| 17 | + Traceback (most recent call last): |
| 18 | + ... |
| 19 | + TypeError |
| 20 | + Note that :func:`only` attempts to advance *iterable* twice to ensure there |
| 21 | + is only one item. See :func:`spy` or :func:`peekable` to check |
| 22 | + iterable contents less destructively. |
| 23 | + """ |
| 24 | + it = iter(iterable) |
| 25 | + first_value = next(it, default) |
2 | 26 |
|
3 |
| -from typing import ( |
4 |
| - Callable, |
5 |
| - Iterable, |
6 |
| - Iterator, |
7 |
| - Optional, |
8 |
| - Set, |
9 |
| - TypeVar, |
10 |
| - Union, |
11 |
| -) |
12 |
| - |
13 |
| -# Type and type variable definitions |
14 |
| -_T = TypeVar('_T') |
15 |
| -_U = TypeVar('_U') |
16 |
| - |
17 |
| - |
18 |
| -def unique_everseen( |
19 |
| - iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = None |
20 |
| -) -> Iterator[_T]: |
21 |
| - "List unique elements, preserving order. Remember all elements ever seen." |
22 |
| - # unique_everseen('AAAABBBCCDAABBB') --> A B C D |
23 |
| - # unique_everseen('ABBCcAD', str.lower) --> A B C D |
24 |
| - seen: Set[Union[_T, _U]] = set() |
25 |
| - seen_add = seen.add |
26 |
| - if key is None: |
27 |
| - for element in filterfalse(seen.__contains__, iterable): |
28 |
| - seen_add(element) |
29 |
| - yield element |
| 27 | + try: |
| 28 | + second_value = next(it) |
| 29 | + except StopIteration: |
| 30 | + pass |
30 | 31 | else:
|
31 |
| - for element in iterable: |
32 |
| - k = key(element) |
33 |
| - if k not in seen: |
34 |
| - seen_add(k) |
35 |
| - yield element |
| 32 | + msg = ( |
| 33 | + 'Expected exactly one item in iterable, but got {!r}, {!r}, ' |
| 34 | + 'and perhaps more.'.format(first_value, second_value) |
| 35 | + ) |
| 36 | + raise too_long or ValueError(msg) |
| 37 | + |
| 38 | + return first_value |
0 commit comments