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()
Related
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.
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
Recently I was learning the sequence alignment algorithm. After I got the alignment matrix, I could find an optimal path, but I was in trouble when I was looking for multiple optimal paths (backtracking)!
My idea is to store the results of multiple paths with multiple instances, and finally loop through all instances of the base class to get the answer.
I know the following conditions:
What conditions to exit recursion
When do I need to create a new instance and when I don't create it?
But the problem is in the second condition. I don't know how many optimal results there are, and I don't know how many new instances will be created.
So I want to be able to dynamically generate an instance name with a variable.
I don't know how to do this:
# those equivalent to new_instance_name = ResultSeq()
a="new_instance_name"
create_new_instance(a,ResultSeq)
My result base class is ResultSeq:
class KeepRefs(object):
"""
reference:https://stackoverflow.com/questions/328851/printing-all-instances-of-a-class#comment167339_328851
"""
__refs__ = defaultdict(list)
def __init__(self):
self.__refs__[self.__class__].append(weakref.ref(self))
#classmethod
def get_instances(cls):
for inst_ref in cls.__refs__[cls]:
inst = inst_ref()
if inst is not None:
yield inst
class ResultSeq(KeepRefs):
"""
save two
"""
def __init__(self, seq1="", seq2=""):
super(ResultSeq, self).__init__()
self.seq1 = seq1
self.seq2 = seq2
Below is my recursive code:
def multi_backtracking(self, array, i, j, result_seq):
"""
:param array: V, E, F
:param i: row
:param j: col
:param result_seq: new instance of the class ResultSeq
:return: Multiple alignment results
"""
def create_new_obj(name, obj):
"""
I don't know how to do this.
"""
pass
if i == 0 and j == 0:
pass
else:
if array is self.array_V:
if sum(pass_judgement) == 1:
"""
An optimal path without creating a new instance.
"""
self.multi_backtracking(self.array_V, i, j, result_seq)
else:
"""
Multiple paths, need to create a new instance
"""
new_instance_name = "xxx"
create_new_obj(new_instance_name, ResultSeq)
...
if pass_judgement[0]:
result_seq.seq1 = self.origin_seq.seq1[i - 1] + result_seq.seq1
result_seq.seq2 = self.origin_seq.seq2[j - 1] + result_seq.seq2
self.multi_backtracking(self.array_V, i - 1, j - 1, new_instance_name)
if pass_judgement[1]:
self.multi_backtracking(self.array_E, i, j, new_instance_name)
if pass_judgement[2]:
self.multi_backtracking(self.array_F, i, j, new_instance_name)
This is just one of my solutions. If there are better suggestions, I will be happy to accept them, thank you!
You do not need names to store variables - you can use a simple list to store your instances:
class A:
def __init__(self,value):
self.value = value
def __repr__(self):
return f" _{self.value}_ "
def rec(i):
"""Recursive function, returns a list of instances of class A with decreasing
value i"""
if i < 0:
return []
return [A(i)] + rec(i-1)
k = rec(5)
print(k)
Output:
[ _5_ , _4_ , _3_ , _2_ , _1_ , _0_ ]
You can acccess your instances inside your list by indexing:
print(k[2]) # _3_
print(k[2].value + k[3].value) # 5
If you really need names, you can use a dictionary to store them - that is about the same as your existing baseclass KeepRefs does (*):
data = { "Instance1" : A(42), "Instance2" : A(3.141)}
print(data)
print( data["Instance1"].value + data["Instance2"].value )
Output:
{'Instance1': _42_ , 'Instance2': _3.141_ }
45.141
Most of the time when you need user generated "names" for variables you should very strongly reconsider your options.
(*) Your baseclass does not keep non-referenced instances around, a real dict will prevent garbage collecting:
k1 = ResultSeq("A","B")
k2 = ResultSeq("C","D")
k3 = ResultSeq("E","F")
for g in ResultSeq.get_instances():
print(g.seq1, g.seq2)
k2 = None # no instance of k2 anywhere
k3 = None # no instance of k3 anywhere
for g in ResultSeq.get_instances():
print(g.seq1, g.seq2)
A B
C D
E F
A B # 2.print loop after removing instances k2,k3
Documentation:
https://docs.python.org/3/library/weakref.html
I think I'm misusing the concept of subclass. I'm working on a hobby project with Grids and Cells.
What I have, is the implementation of a Cell class, and its subclass HexCell which basically redefines many of the attributes/methods like so:
class Cell:
def __init__(self, row_loc, col_loc):
self.row = row_loc
self.col = col_loc
self.links = set()
self.neighbors = 4*[None]
def __repr__(self):
return f'Cell #({self.row},{self.col})'
def link(self, other, bidir = True):
self.links.add(other)
if bidir: other.links.add(self)
Then I have a subclass that is the HexGrid which follows a similar structure with new parameters.
class HexCell(Cell):
def __init__(self, r_out, th_around):
# I'm indexing Hex cells around a center cell
# instead of by rows and columns; Prefixed hex
# as they follow the hexagon, and not regular polar coordinates.
self.hex_r = r_out
self.hex_th = th_around
self.neighbors = 6*[None]
self.links = set()
def __repr__(self):
return f"HexCell #[{self.hex_r}, {self.hex_th}]"
def bind(self, other, to_dir):
to_dir = to_dir % 6
if (self.neighbors[to_dir] is None):
self.neighbors[to_dir] = other
other.neighbors[to_dir - 3] = self
# Hexagonal grids share neighbors.
other_1 = other.neighbors[to_dir - 2]
if (self.neighbors[to_dir - 1] is None) & (other_1 is not None):
self.bind(other_1, to_dir - 1)
other_5 = other.neighbors[to_dir - 4]
if (self.neighbors[to_dir - 5] is None) & (other_5 is not None):
self.bind(other_5, to_dir - 5)
In this case, the method self.link(other) is shared, but other attributes change from rectangular grid to hexagonal like the locaion from (row, col) to (hex_r, hex_th), or neighbors as a 4-list or 6-list. Thus I'd like these attributes to be dependent on a another cell-type attribute and transferred down to the subclass.
Correct use of subclassing needs to obey the following substitution principle:
If there are some objects x_1 of type T_1 and x_2 of type T_2 such that issubclass(T_2, T_1) == True, then any property that applies to x_1 must also apply for x_2.
In other words, you expect subclassing to implement new behaviours, not to change existing behaviours.
In you example, the change of coordinate system itself is a change of behaviour and thus HexCell should not inherit from Cell.
What you can do is create a base class BaseCell that encapsulates the common behaviour between Cell and HexCell and inherit from it.
class BaseCell:
def __init__(self):
self.links = set()
self.neighbors = []
def add_neighbor(self, other):
self.neighbors.append(other)
def link(self, other, bidirectional=True):
self.links.add(other)
if bidirectional:
other.link(self, bidirectional=False)
class Cell(BaseCell):
def __init__(self, row_loc, col_loc):
self.row = row_loc
self.col = col_loc
super().__init__()
def __repr__(self):
return f'Cell #({self.row},{self.col})'
class HexCell(Cell):
def __init__(self, r_out, th_around):
self.hex_r = r_out
self.hex_th = th_around
super().__init__()
def __repr__(self):
return f"HexCell #[{self.hex_r}, {self.hex_th}]"
def bind(self, other, to_dir):
...
Your Cell class is in fact not an abstract "Cell", but a square cell in two-dimensional space (has exactly 4 neighbours, has "row" and "col" position). Such cell may not be subclassed by a hex cell, because hex cell is just a different type of cell : )
As you noticed, the only common things are link() method and links attribute. If you insist on subclassing, you could create something like:
class LinkedObject():
def __init__(self):
self.links = set()
def link(self, other, bidir = True):
self.links.add(other)
if bidir: other.links.add(self)
class SquareCell(LinkedObject):
# "Cell" class here
class HexCell(LinkedObject):
# HexCell here
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)]