Infinite python generator - python

I have been leaning python and programming for not so long. So you may find my question silly.
I am reviewing generator and try to generate 'yes', 'no' infinitely just to understand the concept.
I have tried this code but having "yes" each time
def yes_or_no():
answer = ["yes","no"]
i=0
while True:
if i >=2:
i=0
yield answer[i]
i+=1
c=next(yes_or_no())
print(c)
print(c)
print(c)
print(c)

yes_no() produces the generator; you want to call next on the same generator each time, rather than printing the same first element over and over.
c = yes_no()
print(next(c))
print(next(c))
# etc.
That said, there's no need for a separate counter; just yield yes, then yield no, then repeat.
def yes_or_no():
while True:
yield "yes"
yield "no"

You need to initialize the generator and then call next on the initialized generator object:
c = yes_or_no()
Now you need to call next on c:
print(next(c))
print(next(c))
In your current code c=next(yes_or_no()):
yes_or_no() will initialize the generator and calling next on it will get the first yes and you're saving that yes as name c
In the next lines, you're just printing same yes referred by c while doing print(c)

While your function does return a generator and it has been stated by others that all you need to do is iterate over it using a loop or calling next in succession. Python provides you a great library called itertools to do exactly this thing; it's called itertools.cycle. This is all the code you need to replicate your functions ability:
def yes_no():
return itertools.cycle(['yes', 'no'])
And just like others have said, a generator can be iterated over using next or a loop.
>>> c = yes_no()
>>> next(c)
'yes'
>>> next(c)
'no'
...

Related

Can I make a for loop go forever? (Python)

If I have code like this:
for i in range(3):
print("Hello")
Is there a way to make it print forever with a for loop?
If your for loop takes its data from a generator that runs forever, sure:
def infinite():
while True:
yield
for __ in infinite():
print("Hello")
You asked if it could be done as a one-liner. Not like this, but with some trickery (and without cheating by just importing it from elsewhere like itertools):
for __ in iter(int, 1):
print("Hello")
This works because int() will just return 0 endlessly, never reaching the sentinel value of 1. I still feel it's cheating a bit though, you'd only ever do this to make the point that it can be done with for - obviously while True: is the way to go if you seriously need this.
You asked how for __ in iter(int, 1): works exactly. The documentation for iter() is fairly clear https://docs.python.org/3/library/functions.html#iter, but the short version is that if two arguments are given, the second acts as a sentinel and the first argument has to be a callable which will be called with no arguments and until the sentinel value is returned, all values up to the sentinel will be yielded from the iterator.
For example:
from random import random
def coin_flip():
return 'heads' if random() > .5 else 'tails'
print(list(iter(coin_flip, 'heads')))
Here, coin_flip() is a function that returns 'heads' or 'tails' at random, with an about 50/50 chance. iter(coin_flip, 'heads') creates an iteratable that will call coin_flip() and yield the result until it returns the sentinel value (which won't be yielded).
So, there's a 50% chance you get [], a 25% chance you get ['tails'], a 12.5% chance you get ['tails', 'tails'], etc.
Another way to look at it, is a mock implementation of iter():
def iter(callable, sentinel):
while (result := callable()) != sentinel:
yield result
This doesn't implement all of iter()s functionality though, since it works differently if you only call it with a single argument.
In the original answer, it works because int() will always return 0, which never becomes 1, so the iterator yields 0 forever.
You can use something like itertools.count:
from itertools import count
for i in count():
print(i)
Is there any reason it needs to be a for loop?
You could just use a while loop,
while True:
print('hello')
To stop it press Ctrl + c on your keyboard.

Are infinite for loops possible in Python? [duplicate]

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)

What does "for i in generator():" do? [duplicate]

This question already has answers here:
What does the "yield" keyword do in Python?
(51 answers)
Closed 7 years ago.
Can someone explain what each step in this does?
I have never seen "for i in X:" used where X is a generator, and I am failing to understand how the i interacts with the function if it's not being inserted between the ().
def fib():
a, b = 0,1
while True:
yield b
a,b = b, a + b
for i in fib():
print(i)
Any function that contains a yield will return a generator. The for-loop runs that generator to return values one at a time.
When you run:
for i in fib():
print(i)
The actual mechanics of running the generator are:
_iterator = iter(fib())
while True:
try:
i = next(_iterator)
except StopIteration:
break
print(i)
As you can see, the i variable is assigned the result of calling next() on the generator to get the next value.
Hope that makes it clear where the i comes from :-)
for just ranges over the vaue of the expression. If the expression calls a function, then its value is whatever is returned from the function, so the for ranges over the result of that function.
Note that here though fib is not a function, it is a generator. It successively yields the value of each step.
for loop generates disposable variable if you use it like above. For example, a list object is used again and again in a loop but a disposable iterator is deleted automatically after use.
And yield is a term like return which is used in functions. It gives a result and use it again in loop.
Your codes give you the number known as fibonacci.
def fib():
a, b = 0,1 #initially a=0 and b=1
while True: #infinite loop term.
yield b #generate b and use it again.
a,b = b, a + b #a and b are now own their new values.
for i in fib(): #generate i using fib() function. i equals to b also thanks to yield term.
print(i) #i think you known this
if i>100:
break #we have to stop loop because of yield.
To understand this you will have to understand what yield keyword does. Please take a look at this: What yield does?
Now you get an idea that fib() is not a function it is a generator.
So in code:
def fib():
a, b = 0,1
while True:
yield b #from here value of b gets returned to the for statement
a,b = b, a + b
for i in fib():
print(i)
Since While never gets a false value. It keeps running.

Generator in Python only returning first element

I am trying to create a generator in python 3.4 using the built in next() function. Here is my current code:
myGenerator = next(t for t in [1,2,3,4])
myGenerator
1
myGenerator
1.....
Whenever I call the generator I built, it only returns 1 each time I call it, which is strange, as I thought that each element in the generator can only be iterated through once. How do I fix this code, so that it will print out 1,2,3,4, in that order? Thanks for the help.
It's a bit nice, because you actually have the generator which you want, which is
(t for t in [1,2,3,4])
but then you run next on it. If you look at its documentation you can see that it's doing exactly what the docs say: you applied next to the generator, it returned an object, and you bound this object to something you called myGenerator (it's probably yours, but it's not a generator). Each time you call it, it evaluates to a simple object.
>>> myGenerator = (t for t in [1,2,3,4])
>>> print next(myGenerator)
1
>>> print next(myGenerator)
2
If you don't know how to use next, I suggest you use yield:
def g(l):
for i,x in enumerate(l):
yield x
if i == len(l):
break
for i in g([1,2,3,4]):
print i
Demo:
1
2
3
4
yield can do same feature.

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