2

I am trying to create a generator that will return the natural numbers in order. This is used to enumerate another generator which will exit upon StopIteration, which seems like the easiest way to do it. However, I cannot find an idiomatic way of creating this generator:

def numbers():
    i = 0
    while True:
        yield i
        i += 1

q = Queue.Queue()
for i in numbers():
    try:
        q.put((i, my_generator.next()))
    except StopIteration:
        break

This does work, but it seems unpythonic to use a while True: in this way.

Is there a standard library function to iterate over the natural numbers?

Program man
  • 393
  • 2
  • 12
  • You urgently need to read the [official Python tutorial](https://docs.python.org/3.4/tutorial), specifically the parts about the `range()` function. – TigerhawkT3 Aug 13 '15 at 21:53
  • 8
    Have a look at `itertools.count`. – Alex Riley Aug 13 '15 at 21:55
  • @tigerhawkt3 Yes, I know about range, but to my knowledge, it cannot create an infinite series, only a finite one. – Program man Aug 13 '15 at 21:55
  • 2
    @ajcr Yes, itertools.count is the function I was looking for. Thank you very much. – Program man Aug 13 '15 at 21:56
  • Just give `range()` a very large end value. – TigerhawkT3 Aug 13 '15 at 21:57
  • 3
    BTW, using a `while True` in a generator like that isn't particularly un-pythonic. In fact, it's what the [documentation](https://docs.python.org/2/library/itertools.html#itertools.count) gives in an equivalent implementation of `itertools.count`. – jme Aug 13 '15 at 22:01
  • @jme It does look like the function I wrote is practically identical to the one you linked. I will keep this in mind, but for this project, itertools.count is better than duplicating its functionality. – Program man Aug 13 '15 at 22:03
  • 2
    Alternatively, why not just `enumerate()` your generator? – TigerhawkT3 Aug 13 '15 at 22:05
  • @TigerhawkT3 This is actually the best solution. I didn't think enumerate could do this, but the scales have fallen from my eyes. If you make this a solution I will accept it. – Program man Aug 13 '15 at 22:08
  • @Programman Cyphase already did! :) – Adam Smith Aug 13 '15 at 22:10
  • 1
    @AdamSmith - Yes, after I mentioned it... again... – TigerhawkT3 Aug 13 '15 at 22:11
  • @TigerhawkT3, after you mentioned `enumerate` again, or, I posted a solution after you mentioned it, again? If the former, where did you mention it the first time? If the latter, to what are you referring? Also, your comment about `enumerate` was at 22:05:58Z, whereas my answer was at 22:05:34Z. Since you brought it up :). – Cyphase Aug 13 '15 at 22:43
  • For the former, I'm referring [to](http://stackoverflow.com/questions/31954016/using-regular-expressions-to-extract-string-from-text-file) [these](http://stackoverflow.com/questions/31975921/regex-works-fine-on-pythex-but-not-in-python) (and to a lesser extent [this](http://stackoverflow.com/questions/31953873/list-of-lists-vs-single-list-memory-usage-in-python)). For the latter, you posted your answer first, then I commented, and then you edited your answer before the five-minute grace period completed. – TigerhawkT3 Aug 13 '15 at 22:52
  • @Cyphase - You know you had an `itertools.count()` solution originally, I know you had an `itertools.count()` solution originally. You know you saw my suggestion of `enumerate()` and quickly edited your post, I know you saw my suggestion of `enumerate()` and quickly edited your post. The only problem I really have with this is that taking someone's idea and pretending it was yours all along is not only incredibly rude, it's plagiarism. Please don't do that in the future. – TigerhawkT3 Aug 14 '15 at 03:26
  • @TigerhawkT3, I don't want to get into an argument; I have no issue with you. But I don't explicitly check every single new comment that comes in between edits to my answers to make sure I don't mention anything that someone else has mentioned. `enumerate()` is a fairly standard thing to use; your seeming certainty that I'm just copying you seems to imply that you think I couldn't have thought of it myself. And hey, you don't know me, so you don't know whether I could or not. But I did. – Cyphase Aug 14 '15 at 03:49
  • @TigerhawkT3, If you mention that `for x in range(n):` is better than using a `while` loop and a counter, and it ends up being a few seconds, or several seconds, or a minute after I mention the same thing, that doesn't mean you're plagiarizing me; it means you and I both know the best way to do that sort of thing in Python. I'm sorry that you feel like I've cheated you in some way. I suppose whether I have or not is a matter of opinion, but it certainly wasn't my intent. – Cyphase Aug 14 '15 at 03:50
  • @TigerhawkT3, And as far as my answer to this question, I don't even remember whether or not I edited it; if you are specifically saying that you _saw it change_, as opposed to just assuming that I changed it, I'm willing to believe that; I certainly do edit answers within the initial window. I do however know that I did not change it because I saw your comment; certainly it wasn't as a reaction to your _answer_, as that would have been outside the initial edit window. – Cyphase Aug 14 '15 at 03:50
  • @TigerhawkT3, I hope we can resolve any issue you have with me, especially since I know you're also active in the `python` tag, and I don't want there to be any bad feelings. If you want to discuss this further, I'm happy to do so in a more appropriate place than these comments. – Cyphase Aug 14 '15 at 03:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/86944/discussion-between-tigerhawkt3-and-cyphase). – TigerhawkT3 Aug 14 '15 at 04:00

2 Answers2

3

To answer your literal question, use itertools.count(). It'll count up from a start value by step, into infinity.

That said, it seems what you actually want to do is this:

for idx, item in enumerate(my_generator):
    q.put((idx, item))
Cyphase
  • 10,336
  • 2
  • 24
  • 31
  • It'd be easier not to unpack and then repack the values – Program man Aug 13 '15 at 22:10
  • 2
    @Programman I actually disagree. This is very clear, while `for indexeditem in enumerate(my_generator): q.put(indexeditem)` is less so. – Adam Smith Aug 13 '15 at 22:11
  • What do you mean by "easier" @Programman? It's certainly less readable. – Cyphase Aug 13 '15 at 22:14
  • @Cyphase I meant more readable, but I guess that's a matter of opinion. – Program man Aug 13 '15 at 22:17
  • Because the enumerate already makes it obvious that it's being numbered, so it looks like I'm pushing the now numbered values onto the queue. I don't think this version is particularly unreadable, but I like the idea of the little numbered ``packets'' being put onto the queue after they have been assembled by enumerate. – Program man Aug 13 '15 at 22:21
  • Fair enough; I see where you're coming from. I think my choice could be affected by the semantics of the code. – Cyphase Aug 13 '15 at 22:30
0

You have a couple easy options.

Use a large range():

q = Queue.Queue()
for i in range(10**10):
    try:
        q.put((i, my_generator.next()))
    except StopIteration:
        break

Or simply enumerate() the generator you're actually interested in:

q = Queue.Queue()
for item in enumerate(my_generator):
    q.put(item)
ozgur
  • 41,172
  • 16
  • 73
  • 106
TigerhawkT3
  • 44,764
  • 6
  • 48
  • 82
  • 1
    This could be Python 2; `range()` would return a very large list. Why not just recommend `itertools.count()`? It's the better option anyway (in that code). Obviously the `enumerate` way is best. – Cyphase Aug 13 '15 at 22:13
  • Yes, it could be Python 2... or it could be Python 3. If the question doesn't specify Python 2, I assume it uses the actively-developed version of Python, which is Python 3. – TigerhawkT3 Aug 13 '15 at 22:15
  • Sure, I treat Python 3 as the default, too. But I also try to make my code work in Python 2 as well. Someone might use your suggestion in Python 2 and get a less-than optimal result. Of course, `range(10**10)` is likely to give an error anyway on Python 2. At the very least you should mention the issue. Anyway, this is a bit of a pointless discussion, because again, `itertools.count()` is the better choice all-around. – Cyphase Aug 13 '15 at 22:37
  • @ozgur - Thanks for adding the `()`. I hadn't double-checked the question's code before copy-pasting it. – TigerhawkT3 Aug 13 '15 at 23:25