Defining multiple variances of __iter__ in a class - python

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)

Related

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

Create an insert and pop method on list class

I need to implement an insert method (insert(self, index, val)), that inserts val before index, and a pop method (pop(self)), that removes the last element from mylist, onto the MyList class. The behavior should be identical to the methods already available in python.
Note: For the insert method, similarly with the append method already done, the capacity of the array should be doubled if there is no room for an additional element. The pop method should return the element removed from the list, and put None
in its place in the array. If pop was called on an empty list, an IndexError
exception should be raised.
My code thus far:
import ctypes # provides low-level arrays
def make_array(n):
return (n * ctypes.py_object)()
class MyList:
def __init__(self):
self.data = make_array(1)
self.capacity = 1
self.n = 0
def __len__(self):
return self.n
def append(self, val):
if(self.n == self.capacity):
self.resize(2 * self.capacity)
self.data[self.n] = val
self.n += 1
def resize(self, new_size):
new_array = make_array(new_size)
for ind in range(self.n):
new_array[ind] = self.data[ind]
self.data = new_array
self.capacity = new_size
def extend(self, other):
for elem in other:
self.append(elem)
def __getitem__(self, ind):
if not(0 <= ind <= self.n - 1):
raise IndexError('MyList index is out of range')
return self.data[ind]
def __setitem__(self, ind, val):
if not(0 <= ind <= self.n - 1):
raise IndexError('MyList index is out of range')
self.data[ind] = val
mylst1 = MyList()
for i in range(5):
mylst1.append(i)

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

Python Iterate 2D array __iter__

I am trying to create a 2D Array class and want to make the matrix iterable. Does anyone know how to go about doing this? I am a new to classes in python and still getting the hang of things. Also, how would str be implemented in my main? If i just use print(customMatrix) will that go and use the str method?
Heres what I'm working on. I know some of it isn't correct :)
class Array2D():
def __init__(self, height, width):
self._width = width
self._height = height
self.matrix = [[None]*height for _ in range(width)]
def width(self):
return self._width
def height(self):
return self._height
def set(self, row, col, element):
self.matrix[row][col] = element
def get(self, row, col):
return self.matrix[row][col]
def row(self, row_no):
rowList = []
for item in self.matrix[row_no][item]:
rowList.append(item)
def column(self, col_no):
colList = []
for item in self.matrix[item][col_no]:
colList.append(item)
def __str__(self):
#for row in self.matrix:
#print (row)
return (str(self.matrix))
def __iter__(self):
Actually there is a yield keyword which is handy in this case, It can be thought of as a return statement which saves the previous state while returning anything ,so when you call the method it first yields self.matrix[0][0] on the second call to this method it would yield self.matrix[0][1] and so on..
class Array2D():
def __init__(self, height, width):
self._width = width
self._height = height
self.matrix = [[None]*height for _ in range(width)]
def width(self):
return self._width
def height(self):
return self._height
def set(self, row, col, element):
self.matrix[row][col] = element
def get(self, row, col):
return self.matrix[row][col]
def row(self, row_no):
rowList = []
for item in self.matrix[row_no][item]:
rowList.append(item)
def column(self, col_no):
colList = []
for item in self.matrix[item][col_no]:
colList.append(item)
def __str__(self):
return_str = ""
for i in xrange(self._height):
for j in xrange(self._width):
return_str+=str(self.matrix[i][j])+" " #4 space between each element
return_str+="\n"
return return_str
def __iter__(self):
for i in xrange(self._height):
for j in xrange(self._width):
yield self.matrix[i][j]
def __iter__(self):
for x in xrange(self.width):
for y in xrange(self.height):
yield self.matrix[x][y]
Read up on generators and the yield keyword, they're fun to use!

how to create a between matrix calculations class in python?

I am trying to create a class that will be able to make all possible calculations between matrix. the input is a matrix (doesn't matter the size), and on that matrix the class should do any calculations - either multiply, combine or substract. The different functions are as follow:
I need to have in this class:
__init__(self, data)
get_width(self)
get_height(self)
add(self, m)
scalar_multiply(self, a)
subtract(self, m)
multiply(self, m)
compare(self, m)
Thanks alot, it also needs to be with simple actions.
This is my code till now:
class Matrix:
def __init__(self, data):
self.data = data
def get_width(self):
return len(self.data[0])
def get_height(self):
return len(self.data)
def add(self, m):
lines = []
for j in range(len(self.data)):
line = []
for i in range(len(self.data[j])):
line.append(self.data[j][i] + m[j][i])
lines.append(line)
return lines
def scalar_multiply(self, a):
res = []
for j in range(len(self.data)):
line = []
for i in range(len(self.data)):
line.append(self.data[j][i]*a)
res.append(line)
return res
def subtract(self, m):
lines = []
for j in range(len(self.data)):
line = []
for i in range(len(self.data)):
line.append(self.data[j][i] - m[j][i])
lines.append(line)
return lines
def multiply(self, m):
lines = []
for j in range(len(self.data-1)):
line = []
for i in range(len(m[0])-1):
schum = 0
for k in range(len(self.data[0]-1)):
schum = self.data[j][k]*m[k][i]
line.append(schum)
lines.append(line)
return lines
def compare(self, m):
for j in range(len(self.data)):
for i in range(len(self.data)[j]):
while self.data[j][i] == m[j][i]:
return True
else:
return False
Do you need to create this yourself? If not, there is already an excellent implementation of this in numpy

Categories

Resources