Repository: more-itertools/more-itertools (python)

Issue:
Sync with itertools recipes:  loops()
Recently a new `loops()` recipe was added to the [CPython main docs](https://docs.python.org/3/library/itertools.html#itertools-recipes).

```
def loops(n):
    "Loop n times. Like range(n) but without creating integers."
    # for _ in loops(100): ...
    return repeat(None, n)
```

Simple test:

```
>>> all(list(loops(n)) == [None] * n for n in range(-10, 10))
True
```

It is intended to be used like this:

```
>>> for _ in loops(5):
...     print('hello world')
... 
hello world
hello world
hello world
hello world
hello world
```

Pull request description:
Add loops recipe
This PR closes #923. The `loops` function from the `itertools` docs is added - thanks @rhettinger for the contribution

Patch:
diff --git a/docs/api.rst b/docs/api.rst
index 056ebe7..98188c6 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -328,3 +328,4 @@ Others
 .. autofunction:: tabulate
 .. autofunction:: repeatfunc
 .. autofunction:: reshape
+.. autofunction:: loops
diff --git a/more_itertools/recipes.py b/more_itertools/recipes.py
index 6153414..cf61fc6 100644
--- a/more_itertools/recipes.py
+++ b/more_itertools/recipes.py
@@ -45,6 +45,7 @@ __all__ = [
     'is_prime',
     'iter_except',
     'iter_index',
+    'loops',
     'matmul',
     'ncycles',
     'nth',
@@ -1146,3 +1147,17 @@ def is_prime(n):
     else:
         bases = [randrange(2, n - 1) for i in range(32)]
     return all(_strong_probable_prime(n, base) for base in bases)
+
+
+def loops(n):
+    """Returns an iterable with *n* elements for efficient looping.
+    Like ``range(n)`` but doesn't create integers.
+
+    >>> i = 0
+    >>> for _ in loops(5):
+    ...     i += 1
+    >>> i
+    5
+
+    """
+    return repeat(None, n)
diff --git a/more_itertools/recipes.pyi b/more_itertools/recipes.pyi
index f07ea46..1f73af9 100644
--- a/more_itertools/recipes.pyi
+++ b/more_itertools/recipes.pyi
@@ -137,3 +137,4 @@ def totient(n: int) -> int: ...
 def _shift_to_odd(n: int) -> tuple[int, int]: ...
 def _strong_probable_prime(n: int, base: int) -> bool: ...
 def is_prime(n: int) -> bool: ...
+def loops(n: int) -> Iterator[None]: ...


Test patch:
diff --git a/tests/test_recipes.py b/tests/test_recipes.py
index 0aad5f5..6f8bfaf 100644
--- a/tests/test_recipes.py
+++ b/tests/test_recipes.py
@@ -1370,3 +1370,10 @@ class PrimeFunctionTests(TestCase):
         ):
             with self.subTest(n=n):
                 self.assertTrue(mi.is_prime(n))
+
+
+class LoopsTests(TestCase):
+    def test_basic(self):
+        self.assertTrue(
+            all(list(mi.loops(n)) == [None] * n for n in range(-10, 10))
+        )