New GitHub issue #92628 from pochmann:<br>

<hr>

<pre>
The [Itertools Recipes](https://docs.python.org/3/library/itertools.html#itertools-recipes) have this recipe:
```python
def all_equal(iterable):
    "Returns True if all the elements are equal to each other"
    g = groupby(iterable)
    return next(g, True) and not next(g, False)
```
The `next(g, True)` tries to read a first group and if there isn't one, then... uh... actually `next(g, True)` is *always* true, so that's squeezed into the `return` expression just for the *side effect* of skipping the first group. Psych!

I think for official recipes, it would be better to write clearer code. Two ideas for replacing it:

Proposal 1:
```python
def all_equal(iterable):
    "Returns True if all the elements are equal to each other"
    g = groupby(iterable)
    next(g, None)
    return not next(g, False)
```
This makes the first `next` call a statement that clearly indicates we're not using its result and just do it for skipping the first group. I also replaced `True` with `None` to further indicate this.

Proposal 2:
```python
def all_equal(iterable):
    "Returns True if all the elements are equal to each other"
    g = groupby(iterable)
    return not next(g, False) or not next(g, False)
```
This reads as *"There is no first group or there is no second group"*. This actually *uses* the result of the first `next(g, False)`: If it shows that there is no first group, then the function returns `True` immediately. It doesn't even try to look for a second group (unlike the original recipe and my first proposal, which both do). Also, it's nice to have the exact same expression `not next(g, False)` twice.

Really my point is clarity, but for completeness let me also show benchmarks. My first proposal was faster than the original in every case, and the most significant difference is my second proposal being faster for empty iterables.

Python 3.10.4 on Debian (on a Google Compute Engine instance):
```
iterable = ()
 235 ns   235 ns   235 ns  proposal2 True
 265 ns   265 ns   265 ns  proposal1 True
 265 ns   265 ns   265 ns  original True

iterable = (1,)
 308 ns   308 ns   308 ns  proposal1 True
 314 ns   314 ns   314 ns  original True
 316 ns   316 ns   316 ns  proposal2 True

iterable = (1, 2)
 361 ns   361 ns   361 ns  proposal1 False
 366 ns   366 ns   366 ns  original False
 384 ns   384 ns   384 ns  proposal2 False
```

Python 3.10.4 on Windows (my laptop):
```
iterable = ()
 926 ns   928 ns   929 ns  proposal2 True
1063 ns  1065 ns  1065 ns  proposal1 True
1067 ns  1068 ns  1069 ns  original True

iterable = (1,)
1225 ns  1228 ns  1230 ns  proposal1 True
1251 ns  1253 ns  1253 ns  original True
1257 ns  1258 ns  1261 ns  proposal2 True

iterable = (1, 2)
1397 ns  1409 ns  1410 ns  proposal1 False
1417 ns  1417 ns  1418 ns  proposal2 False
1427 ns  1429 ns  1432 ns  original False
```
The benchmark code:
```python
from timeit import timeit
from random import shuffle
from bisect import insort
from itertools import groupby

def original(iterable):
    "Returns True if all the elements are equal to each other"
    g = groupby(iterable)
    return next(g, True) and not next(g, False)

def proposal1(iterable):
    "Returns True if all the elements are equal to each other"
    g = groupby(iterable)
    next(g, None)
    return not next(g, False)

def proposal2(iterable):
    "Returns True if all the elements are equal to each other"
    g = groupby(iterable)
    return not next(g, False) or not next(g, False)

funcs = [original, proposal1, proposal2]

for iterable in (), (1,), (1, 2):
    print(f'{iterable = }')
    times = {func: [] for func in funcs}
    for _ in range(1000):
        shuffle(funcs)
        for func in funcs:
            number = 1000
            t = timeit(lambda: func(iterable), number=number) / number
            insort(times[func], t)
    for func in sorted(funcs, key=times.get):
        print(*('%4d ns ' % round(t * 1e9) for t in times[func][:3]), func.__name__, func(iterable))
    print()
```

</pre>

<hr>

<a href="https://github.com/python/cpython/issues/92628">View on GitHub</a>
<p>Labels: docs</p>
<p>Assignee: </p>