Title: itertools.repeat does not accept None as an explicit count
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.8
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: chepner, corona10, rhettinger, terry.reedy
Priority: normal Keywords: patch

Created on 2018-07-20 14:11 by chepner, last changed 2018-07-21 14:10 by chepner. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 8355 closed corona10, 2018-07-20 16:47
Messages (7)
msg322010 - (view) Author: Clint Hepner (chepner) Date: 2018-07-20 14:11
I expected to be able to pass None as an explicit count to itertools.repeat to get the default behavior of an infinite iterator:

    >>> list(islice(repeat(1), 10))
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    >>> list(islice(repeat(1, None), 10))
    Traceback (most recent call last):
      File "<stdin>", line 1 in <module>
    TypeError: 'NoneType' object cannot be interpreted as an integer
msg322029 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2018-07-20 16:59
IMHO, this issue should be fixed since
times=None can be passed,

This documentation also shows the equivalent python code,
From this code, times=None should work but does not work in current implementation.

def repeat(object, times=None):
    # repeat(10, 3) --> 10 10 10
    if times is None:
        while True:
            yield object
        for i in range(times):
            yield object

I've upload PR with this issue.
msg322042 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2018-07-21 00:17
Clint, why do you think you need this?

Dong-hee, the pure python code is listed a rough equivalent, not an exact equivalent.
msg322054 - (view) Author: Dong-hee Na (corona10) * (Python committer) Date: 2018-07-21 00:59
Raymond, when I first read this issue, it seemed very clear that it should not work. However, after reading the documentation I misunderstood that it can be worked because the pseudocode specifies NoneType. 

IMHO, in that case, we should let users through documentation know that this code is just pseudo-code and that the actual counts parameter should be passed as an integer type.
msg322068 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2018-07-21 03:04
> we should let users through documentation know that this code is just pseudo-code 

We already modified the docs to specifically say that the provided code is just a rough equivalent.

Thanks for the suggestion, but I'm going to decline.  There doesn't seem to be a legitimate use case for this.
msg322071 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2018-07-21 03:31
The 'roughly equivalent' Python code is real code that really runs, and in that sense not pseudocode.  itertools is coded in C, which allows optional args with no default.  To have optional args when coding in Python, either a default is needed or the argument has to be turned into a residual args tuple of length 0 or 1.  In the former case, 'var is None' should be read as being the equivalent of 'var is undefined' in the C code.

In the latter case, the best equivalent, which has its own noise, might be 

def repeat(object, *times):
    if not times:
        while True:
            yield object
    elif len(times) == 1:
        for i in range(times[0]):
            yield object
        raise TypeError(f'There can be at most 1 times argument, not {len(times)}')

I prefer what we have.
msg322099 - (view) Author: Clint Hepner (chepner) Date: 2018-07-21 14:10
This came up in response to

I realize the current documentation is informative, not normative, but I think
there is a legitimate reason to allow an explicit None argument like the pure Python suggests.

Currently, there is no way to trigger the default behavior explicitly, as repeat behaves more like iter (in that the number of arguments has special meaning) than like islice (whose optional arguments behave like regular parameters with default values).

For example, these two calls are equivalent:

    islice(itr, 5) 
    islice(itr, 5, None)

For some functions, it makes sense for the number of arguments to be significant. For example, there is no meaningful default value for the second argument to iter(), since the second argument completely changes the semantics of the call.

    iter({'a': 1, 'b': 2})  # Return an iterator for the argument
    iter(lambda f:, '')  # Call a function until it returns ''

As an another example, the first argument to map() determines how many
additional arguments are needed.

    map(lambda x: x + 1, [1,2,3])           # yield values form [2,3,4]
    map(lambda x: x + y, [1,2,3], [1,2,3])  # yield values from [2,4,6]

However, with repeat(), it makes sense to think of the second argument as an upper limit that can be either finite or infinite. Lacking an infinite integer value, None makes sense if you read it as "no upper limit"

    repeat(1)         # infinite stream of 1s
    repeat(1, 10322)  # a finite stream
    repeat(1, None)   # proposed: an infinite stream of 1s

I prefer the current description of

    def repeat(object, times=None):


    def repeat(object, *times)

because it accurately reflects the number of arguments you can pass to repeat.
Date User Action Args
2018-07-21 14:10:32chepnersetmessages: + msg322099
2018-07-21 03:31:28terry.reedysetversions: - Python 3.7
nosy: + terry.reedy

messages: + msg322071

type: enhancement
2018-07-21 03:04:04rhettingersetstatus: open -> closed
resolution: not a bug
messages: + msg322068

stage: patch review -> resolved
2018-07-21 00:59:38corona10setmessages: + msg322054
2018-07-21 00:17:09rhettingersetmessages: + msg322042
2018-07-20 19:19:52corona10setversions: + Python 3.8
2018-07-20 16:59:44corona10setnosy: + corona10
messages: + msg322029
2018-07-20 16:47:14corona10setkeywords: + patch
stage: patch review
pull_requests: + pull_request7890
2018-07-20 15:23:56serhiy.storchakasetassignee: rhettinger

nosy: + rhettinger
2018-07-20 14:11:03chepnercreate