Program entering an infinite loop - python

I am new to programming so this might come off as a silly question but can you help me with this code..... I am trying to print just the first ten numbers after the number 'self.num' but somehow it is just entering an infinite loop which I am having to terminate
class TopTen:
def __init__(self, num):
self.num = num
def __iter__(self):
return self
def __next__(self):
if self.num <= self.num+10:
val= self.num
self.num += 1
return val
else:
raise StopIteration
values = TopTen(5)
for i in values:
print(i)

You can implement it like this:
class TopTen:
def __init__(self, num):
self.num = num
self.maxNum = num+10
def __iter__(self):
return self
def next(self):
if self.num < self.maxNum:
val = self.num
self.num += 1
return val
else:
raise StopIteration
for i in TopTen(5):
print(i)
# Prints
# 5
# 6
# 7
# 8
# 9
# 10
# 11
# 12
# 13
# 14
The reason why you have infinite loop is because if self.num <= self.num + 10: will always evaluate to True, as you increase self.num by 1 each time by calling next(), so self.num + 10 will always be higher than self.num.

The condition test if self.num <= self.num+10: in your class will always be true, thats the reason for the infinite loop:

Related

How to return only numbers that divide by 3 in iterable form within class using iterator

I've started to learn iterators am trying to implement them myself.
I have created a class that should provide numbers within a range from user defined start to user defined end, in an iterable form.
Now my code looks like this:
class Can_be_divided_by_three:
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.start > self.end:
raise StopIteration
item = self.start
self.start += 1
if item % 3 == 0:
return item
iterator = Can_be_divided_by_three(3, 8)
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
And this is the output:
3
None
None
6
So actually there is output even if the number is not divided by 3 and it is None.
Am I getting this wrong, and if yes, how to get it right? I actually need the only output in the form of number divisible by 3 with iteration capabilities.
Thank you in advance.
As per your logic next method will return the number if the number is divisible by 3 but you have not specified what this function should do if the number is not divisible by three, so try below code:
class Can_be_divided_by_three:
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.start > self.end:
raise StopIteration
item = self.start
self.start += 1
if item % 3 == 0:
return item
else:
return self.__next__()
iterator = Can_be_divided_by_three(3, 8)
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
Based on John Coleman's comment that you only need to find the smallest multiple of 3, you can achieve the same with this:
def Can_be_divided_by_three(start, end):
while start % 3:
start += 1
for i in range(start, end, 3):
yield i
You only return item if item % 3 == 0.
You should return something else in other scenarios if you want to avoid Nones
The code which you have written is right and would return number divisible by 3 within user defined range. The reason you are finding None in the output is because of the print() function.
Try running the only next(iterator). In Jupyter the iterator returns only 3 & 6

How to update a value after it is returned in python

Consider this piece of code, wondering would it be possible to return a value before it gets updated.
class A:
def __init__(self):
self.n = 0
def get_next(self):
return self.n++ # Return its current value. after it gets returned, update n.
a = A()
a.get_next() # return 0
a.get_next() # return 1
This'll work:
def get_next(self):
old_value = self.n
self.n += 1
return old_value

Defining multiple variances of __iter__ in a class

I am writing a game of Connect 4 and have a Grid class that resembles the 2-dimensional grid that the game is played in. The underlying data structure of my Grid class is an instance variable self.grid - a 2-dimensional list.
I have overwritten the __iter__ method as such:
def __iter__(self):
for row in range(self.numRows):
for col in range(self.numCols):
yield self.grid[row][col]
which I can then call as:
for cell in grid:
# do something with yielded cell
At some points, I need to iterate over just a specified row or a specified column. Instead of writing
col = 0
for row in range(grid.numRows):
# do something with grid.grid[row][col]
is there any Pythonic, readable way to define a 2nd and 3rd __iter__ method along the lines of:
def __iter__(self, col)
for row in range(self.numRows):
yeild self.grid[row][col]
which would be called as:
for row in grid(col=0):
# do something with the yielded cell
and then the same, but with a specified row?
You can certainly have as many of these methods as you like. You just can't call them all __iter__(), because Python does not support method overloading. You could, for example, do this:
def rows(self, col):
for row in range(self.numRows):
yield self.grid[row][col]
def cols(self, row):
for col in range(self.numCols):
yield self.grid[row][col]
def cells(self):
for row in range(self.numRows):
for col in range(self.numCols):
yield self.grid[row][col]
You would then write something like
for row in grid.rows(col=0):
# Do something
Sure you can do that, no need to add special methods. Just add iterators, python 3 code below:
ALL = 0
ROW = 1
COL = 2
class db:
def __init__(self, n):
self.n = n
def __iter__(self):
return db_iter_all(self.n)
def __call__(self, what = ALL):
if what == ALL:
return db_iter_all(self.n)
if what == ROW:
return db_iter_row(self.n)
if what == COL:
return db_iter_col(self.n)
raise ValueError("What what?")
class db_iter_all:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.i > self.n:
raise StopIteration()
r = self.i
self.i += 1
return r
class db_iter_row:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.i > self.n:
raise StopIteration()
r = self.i
self.i += 3
return r
class db_iter_col:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.i > self.n:
raise StopIteration()
r = self.i
self.i += 5
return r
And simple example how to use it
grid = db(15)
for k in grid:
print(k)
for k in grid(ROW):
print(k)
for k in grid(COL):
print(k)

Using an iterator in python?

I have just learned about iterators in Python however I am having a hard time implementing them.
I am trying to write a class to so that this loop works:
odds = OddNumbers(13)
for i in odds:
print(i)
I want to write an iter() function and next() function to do this.
So far I have:
class OddNumbers:
def __init__(self, number):
self.number = number
def __iter__(self):
return self
def __next__(self):
current = self.number
if self.number%2 == 0:
return current
else:
raise StopIteration
But at the moment this is returning nothing. I expect the output to be
1
3
5
7
9
11
13
Help?
Your object needs to keep track of its state and update it when __next__ is called.
class OddNumbers(object):
def __init__(self, number):
self.current = ...
self.number = number
def __iter__(self):
return self
def __next__(self):
# Update self.current
# If the limit has been reached, raise StopIteration
# Otherwise, return something
You need another variable to track the current number:
def __init__(self, number):
self.number = number
self.current = 1
Then you need to compare it with the ending number, and maybe increment it:
def __next__(self):
if self.current > self.number:
raise StopIteration
current = self.current
self.current += 2
return current
There is probably a much cleaner way of doing this, but here is a quick stab:
class OddNumbers:
def __init__(self, number):
self.number = number
def __iter__(self):
self.current = self.number
return self
def __next__(self):
if self.current > 0:
if self.current % 2 == 0:
self.current -= 1
self.current -= 1
return self.current + 1
raise StopIteration
This will give you an iterator-like object which provides even or odd numbers. However, it won't satisfy your for loop semantics as it is not a true iterator.
class NumberIterator(object):
"""Returns simple even/odd number iterators."""
def __init__(self, current):
self.current = current
def next(self):
self.current += 2
return self.current
#classmethod
def getOddIterator(cls):
return cls(-1) # start at 1
#classmethod
def getEvenIterator(cls):
return cls(0) # start at 2
odd_numbers = NumberIterator.getOddIterator()
even_numbers = NumberIterator.getEvenIterator()
odd_numbers.next() # Returns 1
odd_numbers.next() # Returns 3

How to fix this OOP error?

I am trying to understand python oop. but it's not easy for me. thus i wrote
python OOP program (ex.2) for below procedural program (ex.1) but It's not working with below error.
ex.1
def factorial(n):
num = 1
while n >= 1:
num = num * n
n = n - 1
return num
f = factorial(3)
print f # 6
ex.2
class factorial:
def __init__(self):
self.num = 1
def fact(self,n):
while n>=1:
num = self.num * n
n = n-1
return num
f = factorial()
ne= factorial.fact(3)
print(ne)
error
Traceback (most recent call last):
File "F:/python test/oop test3.py", line 13, in ne= factorial.fact(3)
TypeError: fact() missing 1 required positional argument: 'n'
use the instance you created to call the method:
f = factorial() # creates instance of the factorial class
ne = f.fact(3)
Or to call using the class itself without assignment:
ne = factorial().fact(3) # < parens ()
print(ne)
You also have a mistake you should be using self.num or you will always get 1 as the answer, so:
class Factorial: # uppercase
def __init__(self):
self.num = 1
def fact(self, n):
while n >= 1:
self.num = self.num * n # change the attribute self.num
n -= 1 # same as n = n - 1
return self.num
If you don't return your method will return None but you will still increment self.num so if you don't want to return but want to see the value of self.num after you call the method you can access the attribute directly:
class Factorial:
def __init__(self):
self.num = 1
def fact(self, n):
while n >= 1:
self.num = self.num * n
n -= 1
ne = Factorial()
ne.fact(5) # will update self.num but won't return it this time
print(ne.num) # access the attribute to see it
There are three problems:
1) Logical error: num = self.num * n should be changed
to self.num = self.num * n, Here num is another variable which you are creating.
2) Logical error, But If 1st one is addressed it becomes Syntax error:
return num should be changed to return self.num
3) Syntax Error:
f = factorial()
ne= factorial.fact(3)
should be either changed to
ne = factorial().fact(3) or ne = f.fact(3)

Categories

Resources