Loop only iterating once - python

For some reason, the following block of code is only iterating through the for loop once, despite there being 2 entries in the list.
def remove_client(self, client):
try:
temp = client.followedby
for i in temp:
print("Begin")
print(i)
print(client.followedby)
i.unfollow_user()
print(client.followedby)
print("Passed")
print("Out of loop.")
except AttributeError:
print("AttributeError")
pass
self.cur_id[client.id] = False
self.clients.remove(client)
The called function unfollow_user:
def unfollow_user(self):
self.send_host_message("Stopped following at {}.".format(time.asctime(time.localtime(time.time()))))
self.following.followedby.remove(self)
self.following = ""
print("end of unfollow user")
This should work as to my knowledge. It doesn't throw any error, with the console output being:
[<server.client_manager.ClientManager.Client object at 0x000001F87C2CCE80>, <server.client_manager.ClientManager.Client object at 0x000001F87C2CCD30>] Begin <server.client_manager.ClientManager.Client object at 0x000001F87C2CCE80> [<server.client_manager.ClientManager.Client object at 0x000001F87C2CCE80>, <server.client_manager.ClientManager.Client object at 0x000001F87C2CCD30>] end of unfollow user [<server.client_manager.ClientManager.Client object at 0x000001F87C2CCD30>] Passed Out of loop.
What am I doing wrong, here?

This is an example of what you're doing, in a nutshell.
>>> x = [1,2]
>>> for i in x:
... x.remove(2)
... print("Hello world.")
...
Hello world.
When you use the for loop construct in python, you're calling next() on the iterator. The builtin iterator for lists behave like so when you are modifying the elements while iterating:
There is a subtlety when the sequence is being modified by the loop (this can only occur for mutable sequences, i.e. lists). An internal counter is used to keep track of which item is used next, and this is incremented on each iteration. When this counter has reached the length of the sequence the loop terminates.
You are decreasing the length and the iterator checks that. So it exits the loop after only 1 iteration.
If you wanted to run it through all elements, then perform a copy and assign that to your temp:
import copy
temp = copy.copy(x)
for i in temp:
# Do whatever you want here

Related

why i am unable to access 2 generator object in 1 python code

I am unable to use generator for second time. The object is created fine as a generator but I am not getting any values in the list.
CODE:
# list of names
names_list = ['Adam','Anne','Barry','Brianne','Charlie','Cassandra','David','Dana']
# too long
# reverse_uppercase = (name[::-1] for name in (name.upper() for name in names_list))
# breaking it up
upper_case = (name.upper() for name in names_list)
reverse_uppercase = (name[::-1] for name in upper_case)
print("1")
for n in reverse_uppercase:
print(n)
print('break')
for n in upper_case:
print("1")
print(n)
print(upper_case)
print(list(upper_case))
print(reverse_uppercase)
print(list(reverse_uppercase))
Output:
1
MADA
ENNA
YRRAB
ENNAIRB
EILRAHC
ARDNASSAC
DIVAD
ANAD
break
<generator object <genexpr> at 0x7f7434874510>
[]
<generator object <genexpr> at 0x7f74348744a0>
[]
you can see I am trying to acces those generator but I am getting empty list. Please help me.
Generators are one-time objects. This means that if you consumed them once, they are "empty".
Your generator upper_case is consumed in your first for loop, when you consume reverse_uppercase. reverse_uppercase consumes upper_case, because that's how it's defined:
reverse_uppercase = (name[::-1] for name in upper_case)
After
for n in reverse_uppercase:
print(n)
both generators are empty. If you want to be able to iterate the content multiple times, use a list comprehension instead of a generator expression.

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

What happens when closing a loop using an "infinite" iterable?

I have written the following python function(s):
import numpy
def primes_iterable():
"""Iterable giving the primes"""
# The lowest primes
primes = [2,3,5]
for p in primes:
yield p
for n in potential_primes():
m = int(numpy.sqrt(n))
check = True
for p in primes:
if p > m:
break
if n%p == 0:
check = False
if check:
primes.append(n)
yield n
def potential_primes():
"""Iterable starting at 7 and giving back the non-multiples of 2,3,5"""
yield 7
n = 7
gaps = [4,2,4,2,4,6,2,6]
while 1:
for g in gaps:
n += g
yield n
As you can see, both functions don't have a return statement. Suppose I was to write something like this:
for p in primes_iterable():
if p > 1000:
break
print p
What happens at the level of the memory when the break statement is reached? If I understand correctly, calling primes_iterable() makes the function start, go until the next yield and then pause until it is needed again. When the break statement is reached, does the function instance close up, or does it continue existing in the backgroud, completely useless?
Your function primes_iterable is a generator function. When you call it, nothing happens immediately (other than it returning a generator object). Only when next is called on it does it run to the next yield.
When you call the generator function, you get an iterable generator object. If you're doing that in a for loop, the loop will keep a reference to the generator object while it is running. If you break out of the loop, that reference is released and the generator object can be garbage collected.
But what happens to the code running in the generator function when the generator object is cleaned up? It gets interrupted by a GeneratorStop exception thrown in to it at the yield it was paused for. If you need to, you could have your generator function catch this exception, but you can't do anything useful other than cleaning up your resources and exiting. That is is often done with a try/finally pair, rather than an except statement.
Here's some example code that demonstrates the behavior:
def gen():
print("starting")
try:
while 1:
yield "foo"
except GeneratorExit:
print("caught GeneratorExit")
raise
finally:
print("cleaning up")
Here's a sample run:
>>> for i, s in enumerate(gen()):
print(s)
if i >= 3:
break
starting
foo
foo
foo
foo
caught GeneratorExit
cleaning up
When you break from the for loop there is no reference left to the generator so it will eventually be garbage collected...
Just for clarity calling primes_iterable() creates a generator. Calling next() on the generator passes control to the generator and it runs until it yields. The for implicitly calls next() each loop.
Consider this:
prime = primes_iterable()
print(next(prime)) # 2
for p in prime:
if p > 1000:
break
print(p) # 3, 5, 7, ...
Now you still have a reference to the generator called prime so you can always get the next prime:
print(next(prime)) # 1013
primes_iterable() returns an iterator. This is an object which spits out a new value whenever you call next on it. This is what a for loop does behind the scenes. Try this:
it = primes_iterable()
print(next(it))
print(next(it))
Important to note is that it isn't running forever behind the scenes here, it just runs far enough to spit out a new value whenever you ask it to. It keeps hold of its data so that it's ready to start running again whenever, but you can't access that data.
Now, in your code,
for p in primes_iterable():
As above primes_iterable has been called and has returned an iterator, although in this case the iterator has no name (i.e. it is not bound to a variable). For every step of the loop, p will be assigned to next of the iterator.
if p > 1000:
break
Now we break out and the for loop stops running next on the iterator. Nothing references the iterator any more (you can check this by calling dir() which shows you everything defined in the global namespace).
Therefore after a while Python frees up the memory that the iterator was taking up. This is called garbage collection. It's also what will happen if e.g. you type [1,2,3] into the interpreter but don't bind it to a variable name. It is created but then effectively deleted to free up space because it's pointless.
You can (and should) read more about iterators here:
https://docs.python.org/3/tutorial/classes.html#iterators

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)

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

Categories

Resources