Are infinite for loops possible in Python? [duplicate] - python

This question already has answers here:
Looping from 1 to infinity in Python
(8 answers)
Closed 5 months ago.
The community reviewed whether to reopen this question 4 months ago and left it closed:
Original close reason(s) were not resolved
Is it possible to get an infinite loop in for loop?
My guess is that there can be an infinite for loop in Python. I'd like to know this for future references.

You can use the second argument of iter(), to call a function repeatedly until its return value matches that argument. This would loop forever as 1 will never be equal to 0 (which is the return value of int()):
for _ in iter(int, 1):
pass
If you wanted an infinite loop using numbers that are incrementing you could use itertools.count:
from itertools import count
for i in count(0):
....

The quintessential example of an infinite loop in Python is:
while True:
pass
To apply this to a for loop, use a generator (simplest form):
def infinity():
while True:
yield
This can be used as follows:
for _ in infinity():
pass

Yes, use a generator that always yields another number:
Here is an example
def zero_to_infinity():
i = 0
while True:
yield i
i += 1
for x in zero_to_infinity():
print(x)
It is also possible to achieve this by mutating the list you're iterating on, for example:
l = [1]
for x in l:
l.append(x + 1)
print(x)

In Python 3, range() can go much higher, though not to infinity:
import sys
for i in range(sys.maxsize**10): # you could go even higher if you really want but not infinity
pass

Here's another solution using the itertools module:
import itertools
for _ in itertools.repeat([]): # return an infinite iterator
pass

It's also possible to combine built-in functions iter (see also this answer) and enumerate for an infinite for loop which has a counter:
for i, _ in enumerate(iter(bool, True)):
input(i)
Which prints:
0
1
2
3
4
...
This uses iter to create an infinite iterator and enumerate provides the counting loop variable. You can even set a start value other than 0 with enumerate's start argument:
for i, _ in enumerate(iter(bool, True), start=42):
input(i)
Which prints:
42
43
44
45
46
...

Python infinite for loop
Well, there are a few ways, but the easiest one I found is to Iterate over a list
To understand this you must be knowing about:
python basics
python loops
python lists ( and also its append() function
The syntax is something like this...
l = ['#'] # Creating a list
for i in l: # Iterating over the same list
print(l)
l.append(i) # Appending the same element
With every iteration, the an element gets appended to the list. This way the loop never stops iterating.
Happy coding : )

While there have been many answers with nice examples of how an infinite for loop can be done, none have answered why (it wasn't asked, though, but still...)
A for loop in Python is syntactic sugar for handling the iterator object of an iterable an its methods. For example, this is your typical for loop:
for element in iterable:
foo(element)
And this is what's sorta happening behind the scenes:
iterator = iterable.__iter__()
try:
while True:
element = iterator.next()
foo(element)
except StopIteration:
pass
An iterator object has to have, as it can be seen, anextmethod that returns an element and advances once (if it can, or else it raises a StopIteration exception).
So every iterable object of which iterator'snextmethod does never raise said exception has an infinite for loop. For example:
class InfLoopIter(object):
def __iter__(self):
return self # an iterator object must always have this
def next(self):
return None
class InfLoop(object):
def __iter__(self):
return InfLoopIter()
for i in InfLoop():
print "Hello World!" # infinite loop yay!

we can actually have a for infinite loop
list = []
for i in list:
list.append(i)
print("Your thing")

i found a way without using yield or a while loop.
my python version is python 3.10.1
x = [1]
for _ in x:
x.append(1)
print('Hello World!')
if you need loop count, you can use i+1:
x = [1]
for i in x:
x.append(i+1)
print(f'Hello {i}')
you should know that this is not really an "infinite" loop.
because as the loop runs, the list grows and eventually, you will run out of ram.

Best way in my opinion:
for i in range(int(1e18)):
...
The loop will run for thousands of years

You can configure it to use a list. And append an element to the list everytime you iterate, so that it never ends.
Example:
list=[0]
t=1
for i in list:
list.append(i)
#do your thing.
#Example code.
if t<=0:
break
print(t)
t=t/10
This exact loop given above, won't get to infinity. But you can edit the if statement to get infinite for loop.
I know this may create some memory issues, but this is the best that I could come up with.

n = 0
li = [0]
for i in li:
n += 1
li.append(n)
print(li)
In the above code, we iterate over the list (li).
So in the 1st iteration, i = 0 and the code in for block will run, that is li will have a new item (n+1) at index 1 in this iteration so our list becomes [ 0, 1 ]
Now in 2nd iteration, i = 1 and new item is appended to the li (n+1), so the li becomes [0, 1, 2]
In 3rd iteration, i = 2, n+1 will be appended again to the li, so the li becomes [ 0, 1, 2, 3 ]
This will keep on going as in each iteration the size of list is increasing.

The other solutions solutions have a few issues, such as:
consuming a lot of memory which may
cause memory overflow
consuming a lot of processor power.
creating deadlock.
using 3rd party library
Here is an answer, which will overcome these problems.
from asyncio import run, sleep
async def generator():
while True:
await sleep(2)
yield True
async def fun():
async for _ in generator():
print("Again")
if __name__ == '__main__':
run(fun())
In case you want to do something that will take time, replace sleep with your desired function.

i'm newbie in python but try this
for i in range(2):
# your code here
i = 0
can improve this code

In Python 2.x, you can do:
my_list = range(10)
for i in my_list:
print "hello python!!"
my_list.append(i)

Related

Python: be sure only to get one element from lambda back [duplicate]

I have a generator function like the following:
def myfunct():
...
yield result
The usual way to call this function would be:
for r in myfunct():
dostuff(r)
My question, is there a way to get just one element from the generator whenever I like?
For example, I'd like to do something like:
while True:
...
if something:
my_element = pick_just_one_element(myfunct())
dostuff(my_element)
...
Create a generator using
g = myfunct()
Everytime you would like an item, use
next(g)
(or g.next() in Python 2.5 or below).
If the generator exits, it will raise StopIteration. You can either catch this exception if necessary, or use the default argument to next():
next(g, default_value)
For picking just one element of a generator use break in a for statement, or list(itertools.islice(gen, 1))
According to your example (literally) you can do something like:
while True:
...
if something:
for my_element in myfunct():
dostuff(my_element)
break
else:
do_generator_empty()
If you want "get just one element from the [once generated] generator whenever I like" (I suppose 50% thats the original intention, and the most common intention) then:
gen = myfunct()
while True:
...
if something:
for my_element in gen:
dostuff(my_element)
break
else:
do_generator_empty()
This way explicit use of generator.next() can be avoided, and end-of-input handling doesn't require (cryptic) StopIteration exception handling or extra default value comparisons.
The else: of for statement section is only needed if you want do something special in case of end-of-generator.
Note on next() / .next():
In Python3 the .next() method was renamed to .__next__() for good reason: its considered low-level (PEP 3114). Before Python 2.6 the builtin function next() did not exist. And it was even discussed to move next() to the operator module (which would have been wise), because of its rare need and questionable inflation of builtin names.
Using next() without default is still very low-level practice - throwing the cryptic StopIteration like a bolt out of the blue in normal application code openly. And using next() with default sentinel - which best should be the only option for a next() directly in builtins - is limited and often gives reason to odd non-pythonic logic/readablity.
Bottom line: Using next() should be very rare - like using functions of operator module. Using for x in iterator , islice, list(iterator) and other functions accepting an iterator seamlessly is the natural way of using iterators on application level - and quite always possible. next() is low-level, an extra concept, unobvious - as the question of this thread shows. While e.g. using break in for is conventional.
Generator is a function that produces an iterator. Therefore, once you have iterator instance, use next() to fetch the next item from the iterator.
As an example, use next() function to fetch the first item, and later use for in to process remaining items:
# create new instance of iterator by calling a generator function
items = generator_function()
# fetch and print first item
first = next(items)
print('first item:', first)
# process remaining items:
for item in items:
print('next item:', item)
You can pick specific items using destructuring, e.g.:
>>> first, *middle, last = range(10)
>>> first
0
>>> middle
[1, 2, 3, 4, 5, 6, 7, 8]
>>> last
9
Note that this is going to consume your generator, so while highly readable, it is less efficient than something like next(), and ruinous on infinite generators:
>>> first, *rest = itertools.count()
🔥🔥🔥
I don't believe there's a convenient way to retrieve an arbitrary value from a generator. The generator will provide a next() method to traverse itself, but the full sequence is not produced immediately to save memory. That's the functional difference between a generator and a list.
generator = myfunct()
while True:
my_element = generator.next()
make sure to catch the exception thrown after the last element is taken
For those of you scanning through these answers for a complete working example for Python3... well here ya go:
def numgen():
x = 1000
while True:
x += 1
yield x
nums = numgen() # because it must be the _same_ generator
for n in range(3):
numnext = next(nums)
print(numnext)
This outputs:
1001
1002
1003
I believe the only way is to get a list from the iterator then get the element you want from that list.
l = list(myfunct())
l[4]

Infinite loops using 'for' in Python [duplicate]

This question already has answers here:
Is there an expression for an infinite iterator?
(7 answers)
Closed 5 years ago.
Why does this not create an infinite loop?
a=5
for i in range(1,a):
print(i)
a=a+1
or this
for i in range(1,4):
print(i)
i=i-1
or this
for i in range(1,4):
print(i)
i=1
Is there any way we can create infinite loops using a for loop? I know there is the while loop for that but I was just curious.
range is a class, and using in like e.g. range(1, a) creates an object of that class. This object is created only once, it is not recreated every iteration of the loop. That's the reason the first example will not result in an infinite loop.
The other two loops are not infinite because, unlike the range object, the loop variable i is recreated (or rather reinitialized) each iteration. The values you assign to i inside the loop will be overwritten as the loop iterates.
Consider a for loop:
for item in iterable:
print(item)
The idea is that as long as iterable is unchanged, we will loop through each and every item inside iterable once. For example,
for item in [3, 2, 1, 666]:
print(item)
will output 3 2 1 666. In particular, we find that range(1, 4) is a easy way to represent an iterable [1, 2, 3]. Thus,
for i in range(1, 4):
print(i)
will output 1 2 3.
Example 1
a=5
for i in range(1,a):
print(i)
a=a+1
In this case, range(1,a) is evaluated once, when the loop begins.
Example 2
for i in range(1,4):
print(i)
i=i-1
In this case, i is reevaluated every loop, before executing the print and i=i-1 statements within the body of the loop.
Example 3
for i in range(1,4):
print(i)
i=1
Just like Example 2, i is reevaluated every loop.
You can't, in this case, update the iterator that your for loop is looping over.
The range in for i in range(a): is actually a function - it takes a value, a, and returns an object that contains the values that it will loop through. Once you've built that object you can change the input variable as much as you'd like, and that object won't change.
Imagine if we made our own similar function called my_range that generates a list (whereas the built in range function generates a range):
def my_range(end):
my_list = []
for i in range(end):
my_list.append(i)
return my_list
Now if we were to use our new function, like so:
a = 4
for i in my_range(a):
print(i)
a += 1
It'd be obvious that we can't update the list object that we're looping over by changing a, because the list that we're looping over has already been made, and isn't being remade on every loop.
Can you make an infinite loop in python? Yes, just add a new entry to the object that you're looping through, e.g.:
my_list = [0]
for i in my_list:
print(i)
my_list.append(i+1)
Now we're updating the object that we're looping over.
for loops and the range(..) object
If you write for i in range(..): Python does not translate this into something like for(int i = 0; i < n; i++) (in the C-programming language family).
Furthermore the range object is constructed once, before the for loop. The range(..) object, does not know which variables have been used to construct it. Once constructed, the range is fixed.
It sees range(..) as an iterable object, and each iteration, it takes the next item the iterable yields. So whether you set the variable or not in the for loop, has no effect for the next iteration.
In python-2.x, range(..) is not a specific object, but a call to construct a list. So if you call range(10) (without the for loop), you get [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].
Why it does not work?
So then why does the examples do not work?
a=5
for i in range(1,a):
print(i)
a=a+1
Here we construct range(..) once. After that, the variables based on which it was constructed can change, since the range(..) object does change anymore. Incrementing a thus will not mean the range object will get larger.
for i in range(1,4):
print(i)
i=i-1
The for loop each time takes the next item of the iterable. So if we first have collected 1 from the range loop, the next iteration, we collect 2. This is regardless what the value of i is.
for i in range(1,4):
print(i)
i=1
For the very same reason: for does not take into account the previous value of i. It only fetches the next item the iterable (here range(..) yields). Since range(..) is fixed, it will simply feed the for loop the next item.
Emulating an infinite loop
So we need to construct an iterable that keeps yielding elements. A way to do this is itertools.count:
from itertools import count
for i in count():
# ...
pass
Or in case you are not interested in any value, we can use repeat as well:
from itertools import repeat
for _ in repeat(None):
# ...
pass
range copies the parameters given to it for internal use. So changes to those afterwards have no effect. Same as with the loop variable, which is only created from the internal values every time.
That's different though if you use a mutable object like a list to iterate over:
a = [1,2,3]
for i in a:
a.append(i)
This loop will indeed run infinitely.
Because a range is either a list (Python2) or a range object both of which are finite. That range is created once before the loop starts. Your loop variable is assigned the next element of the range at the beginning of each iteration, regardless of what you assign it later in the loop body. You need an infinite iterator for an infinite for loop, e.g. itertools.cycle:
from itertools import cycle
for x in cycle(range(5)):
# endless

Making a Doubly Linked list iterable

I can't figure out how to make my doubly linked list's iterableness work correctly when using nested loops.
My code thus far: http://pastebin.com/PU9iFggr
I have attempted to make it iterable:
def __iter__(self):
self.index = 0
return (self)
def next(self):
try:
result = self._findNode(self.index).get()
except IndexError:
self.index = 0
raise StopIteration
self.index += 1
return result
def __getitem__(self, item):
return self._findNode(item).get()
It seems to work if inside one for loop, but not inside of two:
myList = DoublyLinkedList()
myList.append(0)
myList.append(1)
myList.append(2)
myList.append(3)
for i in myList:
print i #works as expected
for i in myList:
for j in myList:
print j #goes forever
I imagine that the issue is that there is only one self.index inside of the object that is being updated by both of the for loops, but I don't know how to fix this.
Containers should be Iterable, not Iterators. Don't implement next on the class itself. Either make __iter__ a generator function, or write a separate class for it to return that wraps the linked list and implements next.
The easiest approach is to define __iter__ as a generator function:
def __iter__(self):
cur = self.head
while cur is not None:
yield cur.value
cur = cur.nextNode
Remove the next function from DoubleLinkedList and that's it. When you try to iterate it with a for loop, the call to the generator function returns a new, independent generator object which then iterates independently of any other generators that may have been requested. And it's much faster than repeated indexing like you were doing (which has to start from the head and traverse every time; the generator saves state as it goes, so it's only traversing one link in the chain for each item yielded).
I think you know very well where the problem is:
1 for i in mylist:
2 for j in mylist:
3 print j
4 # when j loop ends index goes back 0, this is where the infinite
5 # loop is,next line in execution is 1, and the method called is
6 # "next()", it will read linkedlist[0] for the second time (and
7 # then repeat...forever)
in short every time you call next in i loop, it will just return doubleLinkedList[0], it make to progress towards the index exception.
There are a lot of solutions,
1. if all you do in the nested for loop is print j,you can simply just iterate through the length of your linkedlist:
for i in range(len(mylist)): # I see that you already have the __len__ method
for j in mylist:
print j
2.This is my favorite solution: Instead pf implementing an iterator interface,use python generator:
def traverseList(doubly_linked_list):
# select and delete all of your __iter__() and next(), use the following code
index = 0
while True:
try:
yield doubly_linked_list._findNode(index).get()
index += 1
except IndexError:
break
for i in traverseList(mylist):
for j in traverseList(mylist):
# do things, note that I did not create two linked list
# I sort of create two iterators...
you can look up coroutine if you are not too familiar with generators, but basically they have their own stack, so each iterator of your doubly linked list maintains its own index (what you try to achieve in your code)
3.hmmm I am still thinking, I will update if I got any new ideas

How to change index of a for loop?

Suppose I have a for loop:
for i in range(1,10):
if i is 5:
i = 7
I want to change i if it meets certain condition. I tried this but didn't work.
How do I go about it?
For your particular example, this will work:
for i in range(1, 10):
if i in (5, 6):
continue
However, you would probably be better off with a while loop:
i = 1
while i < 10:
if i == 5:
i = 7
# other code
i += 1
A for loop assigns a variable (in this case i) to the next element in the list/iterable at the start of each iteration. This means that no matter what you do inside the loop, i will become the next element. The while loop has no such restriction.
A little more background on why the loop in the question does not work as expected.
A loop
for i in iterable:
# some code with i
is basically a shorthand for
iterator = iter(iterable)
while True:
try:
i = next(iterator)
except StopIteration:
break
# some code with i
So the for loop extracts values from an iterator constructed from the iterable one by one and automatically recognizes when that iterator is exhausted and stops.
As you can see, in each iteration of the while loop i is reassigned, therefore the value of i will be overridden regardless of any other reassignments you issue in the # some code with i part.
For this reason, for loops in Python are not suited for permanent changes to the loop variable and you should resort to a while loop instead, as has already been demonstrated in Volatility's answer.
This concept is not unusual in the C world, but should be avoided if possible.
Nonetheless, this is how I implemented it, in a way that I felt was clear what was happening. Then you can put your logic for skipping forward in the index anywhere inside the loop, and a reader will know to pay attention to the skip variable, whereas embedding an i=7 somewhere deep can easily be missed:
skip = 0
for i in range(1,10):
if skip:
skip -= 1
continue
if i=5:
skip = 2
<other stuff>
Simple idea is that i takes a value after every iteration irregardless of what it is assigned to inside the loop because the loop increments the iterating variable at the end of the iteration and since the value of i is declared inside the loop, it is simply overwritten. You'd probably wanna assign i to another variable and alter it. For e.g,
for i in range(1,10):
if i == 5:
u = 7
and then you can proceed to break the loop using 'break' inside the loop to prevent further iteration since it met the required condition.
Just as timgeb explained, the index you used was assigned a new value at the beginning of the for loop each time, the way that I found to work is to use another index.
For example, this is your original code:
for i in range(1,10):
if i is 5:
i = 7
you can use this one instead:
i = 1
j = i
for i in range(1,10):
i = j
j += 1
if i == 5:
j = 7
also, if you are modifying elements in a list in the for loop, you might also need to update the range to range(len(list)) at the end of each loop if you added or removed elements inside it. The way I do it is like, assigning another index to keep track of it.
list1 = [5,10,15,20,25,30,35,40,45,50]
i = 0
j = i
k = range(len(list1))
for i in k:
i = j
j += 1
if i == 5:
j = 7
if list1[i] == 20:
list1.append(int(100))
# suppose you remove or add some elements in the list at here,
# so now the length of the list has changed
k = range(len(list1))
# we use the range function to update the length of the list again at here
# and save it in the variable k
But well, it would still be more convenient to just use the while loop instead.
Anyway, I hope this helps.

How to loop through a generator

How can one loop through a generator? I thought about this way:
gen = function_that_returns_a_generator(param1, param2)
if gen: # in case the generator is null
while True:
try:
print gen.next()
except StopIteration:
break
Is there a more pythonic way?
Simply
for x in gen:
# whatever
will do the trick. Note that if gen always returns True.
for item in function_that_returns_a_generator(param1, param2):
print item
You don't need to worry about the test to see if there is anything being returned by your function as if there's nothing returned you won't enter the loop.
In case you don't need the output of the generator because you care only about its side effects, you can use the following one-liner:
for _ in gen: pass
Follow up
Following the comment by aiven I made some performance tests, and while it seems that list(gen) is slightly faster than for _ in gen: pass, it comes out that tuple(gen) is even faster. However, as Erik Aronesty correctly points out, tuple(gen) and list(gen) store the results, so my final advice is to use
tuple(gen)
but only if the generator is not going to loop billions of times soaking up too much memory.
You can simply loop through it:
>>> gen = (i for i in range(1, 4))
>>> for i in gen: print i
1
2
3
But be aware, that you can only loop one time. Next time generator will be empty:
>>> for i in gen: print i
>>>
The other answers are good for complicated scenarios. If you simply want to stream the items into a list:
x = list(generator)
For simple preprocessing, use list comprehensions:
x = [tup[0] for tup in generator]
If you just want to execute the generator without saving the results, you can skip variable assignment:
# no var assignment b/c we don't need what print() returns
[print(_) for _ in gen]
Don't do this if your generator is infinite (say, streaming items from the internet). The list construction is a blocking op that won't stop until the generator is empty.
Just treat it like any other iterable:
for val in function_that_returns_a_generator(p1, p2):
print val
Note that if gen: will always be True, so it's a false test
If you want to manually move through the generator (i.e., to work with each loop manually) then you could do something like this:
from pdb import set_trace
for x in gen:
set_trace()
#do whatever you want with x at the command prompt
#use pdb commands to step through each loop of the generator e.g., >>c #continue

Categories

Resources