0

I have a use case where I need to repeat some block of code:

  • Sometimes I need to repeat the code block a finite number of times
  • Sometimes I need to repeat the code block indefinitely.

I am wondering, is there some Python built-in way to:

  • No limit supplied --> code block is repeated via something like a while loop
  • Limit supplied --> code block is repeated via something like a for loop

Note: I know I can use an if statement (depending on if upper limit is present), and switch between a for or while loop. I am trying to not have the code block repeated twice, or it broken out into another function.


Current Implementation

I wrote the below flawed (see below) for_while_hybrid.py using Python 3.8.2.

from typing import Iterator


def iter_for_while(num_yield: int = None) -> Iterator[int]:
    """Generate an incrementing counter.

     This enables one to make a for loop or a while loop, depending on args.

    Args:
        num_yield: Number of times to yield.
            If left as None, the generator makes a while loop
            If an integer, the generator makes a for loop

    """
    counter = 0
    condition = True if num_yield is None else counter < num_yield
    while condition:
        yield counter
        counter += 1
        if num_yield is not None:
            condition = counter < num_yield


upper_limit = 5
for _ in iter_for_while(upper_limit):
    print("Some code block")  # Run 5 times

for _ in iter_for_while():
    print("Some code block")  # Run infinitely

This implementation works.

The downside is, if run for a very long time, I am worried the counter will take up lots of memory, or will eventually max out. My computer is 64-bit, so sys.maxsize = 2 ** 63 - 1 = 9,223,372,036,854,775,807.

Intrastellar Explorer
  • 3,005
  • 9
  • 52
  • 119
  • 2
    "or will eventually max out" Python's integers are bound only by the amount of memory your computer has, so in practice, they're very unlikely to max out – ForceBru Jun 17 '20 at 18:20
  • 1
    You can move the `counter += 1` into the `if num_yield is not None:` clause? – Perfect Jun 17 '20 at 18:26
  • 1
    `sys.maxsize` is only relevant for finding the maximum number of elements that can be stored in, for example, a list. It is not a bound on how large an `int` value can be. – chepner Jun 17 '20 at 18:26
  • You aren't likely to live long enough to see any loop execute 9 quadrillion times. – chepner Jun 17 '20 at 18:27
  • Even `sys.maxsize`, though, only needs 36 bytes of memory, and for comparison, even `0` takes 24 bytes. – chepner Jun 17 '20 at 18:29
  • In other words, memory is *not* an issue, no matter how you structure the iterator. – chepner Jun 17 '20 at 18:29
  • @chepner thanks for pointing out the `sys.maxsize` was not a relevant concern. That makes sense. Thank you also for the answer below! – Intrastellar Explorer Jun 17 '20 at 18:53

2 Answers2

6

Just use count or range, depending on the upper bound:

from itertools import count


def iter_for_while(bound=None):
    return count() if bound is None else range(bound)
chepner
  • 497,756
  • 71
  • 530
  • 681
  • This is a great solution, and much simpler than mine. I also have a suggested improvement. Per the comment on https://stackoverflow.com/a/57003627/11163122, I would use `itertools.repeat(0)`, as opposed to `itertools.count()` – Intrastellar Explorer Jun 17 '20 at 18:52
  • 2
    It makes no practical difference. If the loop runs a billion times, the size of the counter will grow from 24 bytes to 28 bytes. A *trillion* times, and the counter is still only 32 bytes. If that commenter actually saw a memory issue, it wasn't from the counter. – chepner Jun 17 '20 at 18:54
1

use a while loop but you can say while X < Y: do something and then X += 1 this means you can control how many times it repeats with X or if you want it indefinitely then don't say X + 1

Evorage
  • 493
  • 3
  • 15