0

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
Nagamoto
  • 3
  • 2
  • Possibly tangential: you shouldn't use a mutable object as the default value for arguments. See: https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument – rdas Jul 05 '20 at 09:35
  • Is this is a grid? where for each cell you are defining property and for some value each a cell neighbour cell does XYZ operation? – sahasrara62 Jul 05 '20 at 09:38
  • @sahasrara62: Yes but the grid is not defined within this script, but in grasshopper [rhino3d.com](https://www.rhino3d.com/6/new/grasshopper) – Nagamoto Jul 05 '20 at 09:44
  • so what value you can access form grasshopper ? – sahasrara62 Jul 05 '20 at 09:56
  • All you can see in the code above: a value a for each cell, its index and the index of the connected cells to that cell; theoretically one could also get the XYZ position for each cell, but the cells are on a highly non uniform surface – Nagamoto Jul 05 '20 at 10:01
  • @sahasrara62 screenshot of an example: https://ibb.co/yBCL4k2 – Nagamoto Jul 05 '20 at 10:23

2 Answers2

0

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.

alani
  • 12,573
  • 2
  • 13
  • 23
0

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()
  • If you are going to use the class variable approach (which would certainly work, provided that you do not need to maintain separate lists of cells) then it would be worth putting `self.cells.append(self)` inside `cell.__init__` so that instances are appended automatically when they are created, rather than rely on external code (in this case `main`) to populate the list. – alani Jul 05 '20 at 10:35