Python saving value as an instancemethod instead of integer - python

So, I am currently writing a program that randomly generates a grid maze. The following code segment is the class definition of a single "cell" of the grid. I have defined 2 getter methods get_row and get_col, which can retrieve the coordinates of the cell. I believe this is where the issue lies.
class cell: #Node storing each cell of grid
def __init__(self, rowNum=None, colNum=None):
self.rowNum = rowNum
self.colNum = colNum
self.blocked = 0 # 0 denotes False, all cells are initially unblocked
self.f = 0 # Distance from start node
self.g = 0 # Distance from goal node
self.h = 0 # Total cost ( h = f + g )
self.parent = None
def get_row(self):
return self.rowNum
def get_col(self):
return self.colNum
def isBlocked(self):
if self.blocked == 0:
return 0
if self.blocked != 0:
return 1
Then, I use the following segment to traverse the grid and randomly determine certain squares as the start and end. I save these coordinates as start_row, start_col, end_row, and end_col.
# List containing all the unblocked cells
unblockedCells = [cell() for i in range(numUnblocked)]
start_row = -1
start_col = -1
end_row = -1
end_col = -1
# Randomly picks 2 unblocked cells to be the starting and ending point
def set_start_and_end():
global start_row
global start_col
global end_row
global end_col
ctr1 = 0
for i in range(totalRows):
for j in range(totalCols):
if cellsArray[i][j].blocked == 0:
unblockedCells[ctr1] = cellsArray[i][j]
ctr1 = ctr1+1
#Initialize start position
startIndex = random.randint(0,len(unblockedCells)-1)
start_row = unblockedCells[startIndex].get_row
start_col = unblockedCells[startIndex].get_col
#Initialize target position
endIndex = random.randint(0, len(unblockedCells)-1)
end_row = unblockedCells[endIndex].get_row
end_col = unblockedCells[endIndex].get_col
print("Start: (",start_row,", ",start_col,") End: (",end_row,", ",end_col,")")
set_start_and_end()
However, when I try to access the values of start_row and start_col later in the program, I receive the error list indices must be integers, not instancemethod
My question is, why are start_row and start_col being stored as instancemethod and not as integers?

Here's a small snippet to illustrate the different options
class Cell:
"""Doc strings go here btw, not as comments. Also take a look at pep8 naming conventions: https://www.python.org/dev/peps/pep-0008/#naming-conventions"""
def __init__(self, row_num = None, col_num = None):
self.row_num = row_num # A public instance member - you can get and set this directly without the need for function
self._col_num = col_num # A private member. By convention, you should not get or set this directly
def get_row(self):
"""This is a method, you need so call it using parenthesis i.e. .get_row()"""
return self.row_num
#property
def col_num(self):
"""This is property being used to expose a private member. You don't need parenthesis to call it."""
return self._col_num
my_cell = Cell(1, 2)
print(my_cell.row_num) # row_num is a public member, you can just access it directly
print(my_cell.get_row) # get_row is a method, so this will print the actual function
print(my_cell.get_row()) # this calls the function and prints the integer
print(my_call.col_num) # because of the property decorator, we don't have to call this function but can treat is as if it was a member. But note it would be weird to name it get_... now as verbs imply functions.
In your case I would just use a public instance member. There is no need for any of your functions. In fact this whole object looks like it could just have been a named tuple:
from typing import NamedTuple
class Cell(NamedTuple):
row: int
col: int
blocked: bool
or if you're using python>=3.8 a dataclass
from dataclasses import dataclass
#dataclass
class Cell:
row = None
col = None
blocked = False

Related

Why is this instance variable not incrementing (Python)?

I have implemented a Depth-first search algorithm, and using an instance variable called "call-count", I am trying to track how many recursions/comparisons are executed inside the code, i.e. how many times "searchDFS" is ran. Inside "searchDFS", the call_count variable defaults to the instance variable the first time searchDFS is ran, then it is passed down through every recursion. All of the other instance variables work fine, I am able to append to the top-level instance array "final_path" just fine, but it refuses to increment call_count.
What am I doing wrong, and is there a better way to do this?
The code:
class Node:
def __init__(self, id, connections):
self.id = id
self.connections = connections
class DFS:
def __init__(self):
self.visited = {}
self.nodes = []
self.final_path = []
self.call_count = 0
def searchDFS(self, nodes, target, current, call_count=None, final_path=None, visited=None):
if(visited == None): visited = self.visited
if(final_path == None): final_path = self.final_path
if(call_count == None): call_count = self.call_count
call_count +=1 # <<< to increment every time searchDFS is ran
if current in visited.keys():
print(f'already visited node {current}')
return 0
else:
print(f'current -> {current}')
visited[current] = 1
if(current == target):
print('found!')
final_path.append(current)
return True
if(nodes[current].connections):
for node in nodes[current].connections:
if(self.searchDFS(nodes, target, node.id, call_count, final_path, visited)):
final_path.append(current)
if(current == 0):
print(final_path[::-1])
print(f'call count: {call_count}') # not incrementing !!! :(
return True
return False
def setup_graph(self):
for x in range(0, 5):
self.nodes.append(Node(x, []))
self.nodes[0].connections = [self.nodes[1], self.nodes[2]]
self.nodes[1].connections = [self.nodes[0], self.nodes[3]]
self.nodes[2].connections = [self.nodes[0], self.nodes[4]]
self.nodes[3].connections = [self.nodes[1]]
self.nodes[4].connections = [self.nodes[2]]
def print_graph(self):
for node in self.nodes:
print(f"node id: {node.id}")
print("children: ")
for childs in node.connections:
print(childs.id)
def main(self):
print('main() init')
self.setup_graph()
self.print_graph()
self.searchDFS(self.nodes, 4, 0)
if __name__ == "__main__":
DFS().main()
Solved thanks to #slothrop (in-depth explanation)
The difference comes down to "rebinding the name" versus "mutating the value"
Primitives are immutable in Python - They cannot be changed, only new ones created (and the variable name is re-bound to the new value):
x = 5
x = x + 1
The above creates a new block of memory with the new value, 6. Then, x rebinds to that new value. It is similar to how pointers work in C.
Unassigned values are automatically discarded by Python's garbage collector. The memory cell that was holding the '5' will be cleared, since it is no longer assigned to any variable.
Assignment never copies data, so assigning one variable to another won't make a second copy of the first, it just gives it 'directions' on where to look to find the value.
x = 5 # creates a value 5, with 'x' pointing to it
y = x # y is now pointing/bound to that same value, 5, as well
x = x + 1 # a new value, 6, is created, and x is "bound" to it
print(x) # outputs 6
print(y) # outputs 5
'y' is still "bound/looking" at the original '5'. There are also mutable values, like Lists. Lists contain a built-in function to append to themselves.
x = [1, 2]
y = x
x.append(3)
print(y) # outputs [1, 2, 3]
Since List is a class, it can have methods to change in-place, without having to create a completely new value like primitives.
In my code, when I assigned "call_count = self.call_count", I only pointed the value of "call_count" to the same value of instance variable "self.call_count". Incrementing "call_count" only assigns a new value for the local variable "call_count" without re-assigning the original instance variable "self.call_count". To fix this, I just made a function to update the instance's value, and bound a variable to that method inside my search function:
class DFS:
def __init__(self):
self.call_count = 0
def increment_call_count(self):
self.call_count += 1
def searchDFS(self, counter=0, inc=None):
if(inc == None): inc = self.increment_call_count # set once
inc() # execute the function which "inc" is pointing at
if(counter<5):
self.searchDFS(counter + 1, inc)
if(counter == 0): print(self.call_count) # prints 6 correctly
def main(self):
self.searchDFS()
if __name__ == "__main__":
DFS().main()
Since inc is only bound once to a single function, the 'value' of inc never changes, using it will just run that same function, which re-binds the original instance's variable to the new value.

Create x instances of a class and store the order they were created in

I want to create x amount of instances of a class and store (inside the instance) the order they were created in. Is there a way that I can do that?
MaxAnts = 100
class Ant:
def __init__(self,id):
self.id = id
# then I will use the id to do math
i = 0
if i <= MaxAnts:
i += 1
i = Ant(i)
But for some reason this doesn't work.
The class itself can give ids to its instances.
class Ant:
counter = 0 # counter is a class variable
def __init__(self):
cls = type(self)
self.id = cls.counter # id is an instance variable
cls.counter += 1
# example:
a = Ant()
b = Ant()
c = Ant()
print(a.id, b.id, c.id) # prints: 0 1 2
Using list comprehension as described in the comments is not a bad way to go but I'll put this here just to give you a introduction to lists.
MaxAnts = 100
class Ant:
def __init__(self,id):
self.id = id
#then i will use the id to do math
ants = [] # or ants = list() , same thing
i = 0
if i <= MaxAnts:
i += 1
ants.append(Ant(i))
The only wierd thing here is that because lists are 0 indexed ants[0].id will be 1 and ant[99].id will be 100. If you do ants = [Ant(i) for i in range(MaxAnts)] you will have your index lined up with your ids, ants[7].id = 7. But you could get the same effect by putting i+=1 after ants.append(Ant(i)) instead of the line before.
To create x amount of instances of some class you would need to use some form of iteration and append them to a collection that you would be able to access.
MaxAnts = 100
class Ant:
def __init__(self,id):
self.id = id
#then i will use the id to do math
ants = [] # A list to store your ants
for i in range(MaxAnts): # Iterating i from 0 to MaxAnts - 1
ant = Ant(i) # Creating your new ant with the i as a parameter
ants.append(ant) # Adding your new ant to the ants list
Since by default 'range' gives you a range from 0 to argument - 1, in case you want to start your order from 1, you should start your iteration from 1, and end it on MaxAnts:
for i in range(1, MaxAnts + 1): # Iterating i from 1 to MaxAnts
More on lists: https://docs.python.org/3/tutorial/datastructures.html
More on range: https://docs.python.org/3/library/functions.html#func-range

AttributeError in Python when attempting to initialize a class in a class

I am attempting to call a class into a class's initializing method, but I am receiving an attribute error. Does anyone have any ideas why this might be occurring? I was able to do this exact thing with another class, but it doesn't seem to like the Movement() class I made.
The add_piece() method in Board() seems to be the one causing the issues.
class Movement:
"""
Class to help separate movements from the board methods
"""
def __init__(self):
pass
def ind(self, pos):
"""
Converts the string input to an easier index for the board
"""
row = int(pos[1:]) - 1
column = self.letter_to_column(pos)
return row, column
def letter_to_column(self, pos):
"""
Method to convert string letters of columns to int to index
"""
column_dict = {}
column_dict['a'] = 0
column_dict['b'] = 1
column_dict['c'] = 2
column_dict['d'] = 3
column_dict['e'] = 4
column_dict['f'] = 5
column_dict['g'] = 6
column_dict['h'] = 7
column_dict['i'] = 8
return column_dict[pos[0]]
# BOARD -------------------------------------------------------------------
class Board:
"""
Represents the board and its pieces
"""
def __init__(self):
"""
Builds the board and places the pieces in its spots
"""
self._board = []
for i in range(10):
self._board.append([None for i in range(9)])
self.place_pieces()
self._move = Movement()
def add_piece(self, pos, piece):
"""
pos is a string of a letter (column) and a number (row)
for example pos = 'a2' which is the first column in the second row
"""
self._board[self._move.ind(pos)[0]][self._move.ind(pos)[1]] = piece
The issue isn't related to Movement: it's related to a missing method in Board. Board.place_pieces is not defined, so the expression self.place_pieces() in Board.__init__ will throw an AttributeError.
As an asideā€”if the only purpose of the Movement class is to collect related helper functions, you might want to consider making those classmethods or staticmethods instead (which you can read more about here):
class Movement:
COLUMNS = {
letter: index
for index, letter in enumerate("ABCDEFGH")
}
#classmethod
def get_indices(cls, coordinate): # cls is to class methods what self is to instance methods.
# Rough validation of the coordinate:
if not len(coordinate) == 2:
... # Raise some exception.
# Assignment via sequence unpacking
# See https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
column, string_row = coordinate.upper()
# Row should be representable as a digit and
# in a specific range.
row_index = int(string_row)
if row_index < 1 or row_index > 8:
... # Raise an exception.
# If column isn't in ABCDEFGH, the dictionary
# access here will throw (desirable).
return row_index - 1, cls.COLUMNS[column]
Movement.COLUMNS is a class variable generated using a dictionary comprehension. enumerate is a built-in function that we use to return an index for each letter in the string ABCDEFG.
You can then skip the creation of self._mode and write:
row_index, column_index = Movement.get_indices("A2")
in Board.add_piece, for example.

access list of instantiated objects form within the object

I want to access a list of instantiated objects with a method inside the objects' class in Python 3.
I assume I can't give the the whole list to the object, as it would contain itself.
Concretely: how do I access cells[] from within the class cell? Or is this the wrong way to think about it? the end goal is to easily program cell behavior like cell.moveUp() -- all cells are connected to 8 neighbors.
I am missing something, probably since I don't have much experience in python/programming.
#!/usr/bin/env python3
import random
class cell:
""" cell for celluar automata """
def __init__(self, n=0, nghbrs=[], a=0.00, b=0.00, c=0.00):
self.n = n #id
self.nghbrs = nghbrs #list of connected neighbors
self.a = a #a value of the cell
self.b = b
self.c = c
def growUp(self):
if self.a > .7: # if cell is "bright"
cells[self.nghbrs[7]].a = self.a # update cell above (nghbrs[7] = cell above )
def main():
iterations = 4
links = initLinks() # 150 random links [link0, link2, ... , link7]*150
val1 = initval() # 150 random values
cells = [cell(nghbrs[0], nghbrs[1], val1[nghbrs[0]])for nghbrs in enumerate(
links)] # create cell objects, store them in cells and init. neigbours , a
for i in range(iterations): # celluar automata loop
for c in cells:
c.growUp()
def initLinks(): #for stackoverflow; in real use the cells are arranged in a grid
nghbrs = []
for i in range(150):
links = []
for j in range(8):
links.append(random.randrange(0, 150))
nghbrs.append(links)
return nghbrs
def initval():
vals = []
for i in range(150):
vals.append(random.random())
return vals
if __name__ == "__main__":
main()
run as is cells cannot be accessed in the method growUp():
NameError: name 'cells' is not defined
You could make a CellsList class (subclass of list) that has a method which you call to get a new cell.
class CellsList(list):
def add_cell(self, *args, **kwargs):
"""
make a cell, append it to the list, and also return it
"""
cell = Cell(cells_list=self, *args, **kwargs)
self.append(cell)
return cell
then in the cell itself (I've renamed the class Cell and above I am using cell as in instance variable in accordance with usual capitalisation convention) you have an attribute cells_list where you store a back-reference to the cells list. (I'm also fixing the initialisation of nghbrs to avoid a mutable object in the defaults.)
class Cell:
""" cell for celluar automata """
def __init__(self, n=0, nghbrs=None, a=0.00, b=0.00, c=0.00, cells_list=None):
self.n = n #id
self.nghbrs = (nghbrs if nghbrs is not None else []) #list of connected neighbors
self.a = a #a value of the cell
self.b = b
self.c = c
self.cells_list = cells_list
def growUp(self):
if self.a > .7: # if cell is "bright"
self.cells_list[self.nghbrs[7]].a = self.a # update cell above (nghbrs[7] = cell above )
And then inside main, you can change your current code that instantiates Cell (or what you call cell) directly (your line with cells = ...) to instead use cells.add_cell
cells = CellsList()
for nghbrs in enumerate(links):
cells.add_cell(nghbrs[0], nghbrs[1], val1[nghbrs[0]])
Here we're not actually using the value returned by add_cell, but we return it anyway.
Note: this approach allows you to maintain multiple independent lists of cells if you wish, because it does not rely on any class variables to hold the list -- everything is held in instance variables. So for example, your main program could model multiple regions, each containing a different cells list, by instantiating CellsList more than once, and calling the add_cell method of the relevant CellsList instance to create a new cell.
You can track instances of cell() by making the cells list a static variable of your class, which can be easily accessed from within all instances of the class.
import random
class cell:
""" cell for celluar automata """
cells = []
def __init__(self, n=0, nghbrs=[], a=0.00, b=0.00, c=0.00):
self.n = n #id
self.nghbrs = nghbrs #list of connected neighbors
self.a = a #a value of the cell
self.b = b
self.c = c
def growUp(self):
if self.a > .7: # if cell is "bright"
self.cells[self.nghbrs[7]].a = self.a # update cell above (nghbrs[7] = cell above )
def main():
iterations = 4
links = initLinks() # 150 random links [link0, link2, ... , link7]*150
val1 = initval() # 150 random values
cell.cells = [cell(nghbrs[0], nghbrs[1], val1[nghbrs[0]])for nghbrs in enumerate(
links)] # create cell objects, store them in cells and init. neigbours , a
for i in range(iterations): # celluar automata loop
for c in cell.cells:
c.growUp()
def initLinks(): #for stackoverflow; in real use the cells are arranged in a grid
nghbrs = []
for i in range(150):
links = []
for j in range(8):
links.append(random.randrange(0, 150))
nghbrs.append(links)
return nghbrs
def initval():
vals = []
for i in range(150):
vals.append(random.random())
return vals
if __name__ == "__main__":
main()

Python: storing object in 2d array and calling its method

I'm trying my hand at making a chess app. Code below:
#file containing pieces classes
class Piece(object):`
name = "piece"
value = 0
grid_name = "____"
class Pawn(Piece):
# Rules for pawns.
#If first move, then can move forward two spaces
name = "Pawn"
value = 1
grid_name = "_PN_"
first_move = True
#Main file
from Piece import *
class GameBoard:
pieces = []
grid = [][]
def __init__(self):
self.grid[1][0] = self.pieces.append(Pawn())
currentBoard = GameBoard()
I want to call the value variable for the object located at grid[1][0]
It would look something like:
print currentBoard.grid[1][0].value
This code doesn't work which tells me I'm missing something regarding the scope of the objects and variables. Is this something that is possible in Python?
EDIT - Solution
I did find a solution to this in using the grid list to hold a reference to the indexing of the objects in the pieces list. Code below:
class GameBoard:
# initialize empty board
grid = [["____" for i in range(8)] for j in range(8)]
pieces = []
def __init__(self):
self.grid[0][0] = 0
self.grid[0][1] = 1
self.grid[0][2] = 2
self.grid[0][3] = 3
self.grid[0][4] = 4
self.grid[0][5] = 5
self.grid[0][6] = 6
self.grid[0][7] = 7
self.grid[1][0] = 8
self.grid[1][1] = 9
self.grid[1][2] = 10
self.grid[1][3] = 11
self.grid[1][4] = 12
self.grid[1][5] = 13
self.grid[1][6] = 14
self.grid[1][7] = 15
pieces = []
pieces.append(Pawn())
#grid will return the integer which can be passed to the other list to pull an
#object for using the .value attribute
print pieces[currentBoard.grid[1][0]].value
Rewriting your code so that it simply runs as a single file:
#file containing pieces classes
class Piece(object):
name = "piece"
value = 0
grid_name = "____"
class Pawn(Piece):
# Rules for pawns.
#If first move, then can move forward two spaces
name = "Pawn"
value = 1
grid_name = "_PN_"
first_move = True
class GameBoard:
pieces = []
grid = [[],[]]
def __init__(self):
self.grid[0][1] = self.pieces.append(Pawn())
currentBoard = GameBoard()
There are a few things that need to be corrected. For one, the variables defined in Piece, Pawn and GameBoard are not defined under the __init__() method. This means that these variables will be shared by all instances of the class.
Example:
>>> pawn1 = Pawn() # Make two Pawns
>>> pawn2 = Pawn()
>>> print pawn1.first_move, pawn2.first_move
True, True
>>> pawn1.first_move = False # Change the first pawns attribute
>>> print pawn1.first_move, pawn2.first_move # But both change
False, False
To avoid this, define your class attributes under the method __init__() for all three of your classes.
Example:
class Pawn(Piece):
# Rules for pawns.
#If first move, then can move forward two spaces
def __init__(self):
self.name = "Pawn"
self.value = 1
self.grid_name = "_PN_"
self.first_move = True
Next, your variable grid is not properly defined in python. If you wanted a list with two empty lists you could do the following
grid = [[], []]
But an easy way to make an 8x8 structure of empty lists would be to do the following
grid = [[[] for i in xrange(8)] for j in xrange(8)]

Categories

Resources