Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Behaviour of 2D array
#1
My intention is to populate a 2d array with booleans at a certain DENSITY. Although the function "populate" seems to do the right thing up to exiting a double for loop, it seems as if the last row of the array has been stored in all rows, as shown in the output

from array import *
import random

MAXHORZ = 15
MAXVERT = 10
DENSITY = 50

torus = array('b')
torus = [[0] * MAXHORZ] * MAXVERT


def populate_torus(torus):
    for x in range(MAXVERT):
        for y in range(MAXHORZ):
            if random.randint(1, 100) < DENSITY:
                torus[x][y] = 1
            else:
                torus[x][y] = 0
        print(torus[x])
    print()
    for i in range(MAXVERT):
        print(torus[i])
    return(torus)


def display_torus(torus):
    print('In Display:')
    for x in range(0, MAXVERT):
        print(torus[x])
    print('end')


torus = populate_torus(torus)
display_torus(torus)
print('DONE')
The output produced is as follows:

Output:
/usr/bin/python3.8 /tmp/first_run.txt/Torus.py [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1] [0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0] [0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1] [1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0] [1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1] [1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0] [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0] [0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0] [1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] In Display: [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] [1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0] end DONE Process finished with exit code 0
There is obviously something I do not understand about arrays can anyone spot the flaw for me>
buran write Jan-21-2021, 09:53 AM:
Please, use proper tags when post code, traceback, output, etc. This time I have added tags for you.
See BBcode help for more info.
Reply
#2
The problem is how you create it on this line

torus = [[0] * MAXHORZ] * MAXVERT
lists are mutable and you populate with same object, thus when you update one, it is reflected elsewhere

rows = 3
cols = 4

spam = [[0] * cols] * rows
print(spam)
print([id(row) for row in spam])
spam[0][0] = 1
print(spam)
print('-----------------------')
eggs = [[0] * cols for _ in range(rows)]
print(eggs)
print([id(row) for row in eggs])
eggs[0][0] = 1
print(eggs)
Output:
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] [12470912, 12470912, 12470912] [[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]] ----------------------- [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] [13534272, 13534312, 13534152] [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#3
Thanks, I'll try the second form.
I just did, works to perfection. Grateful!
SB
Reply
#4
You can also create the data structure on-the fly, without need to initialize it beforehand
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#5
(Jan-21-2021, 10:42 AM)buran Wrote: You can also create the data structure on-the fly, without need to initialize it beforehand
I see! How? (I come back to programming for the first time after 30 years - used to be a C programmer where the notion of "on the fly" is peculiar). In this particular case it is probably best to declare it for readability as this array is involved almost everywhere (it is a toroid surface for my own variation of Conway's game of Life).
Reply
#6
what I mean:
import random

def create_torus(max_vert, max_horz, density):
    torus = []
    for x in range(max_vert):
        torus.append([])
        for y in range(max_horz):
            torus[-1].append(int(random.randint(1, 100) < density))
    return torus

def pprint_torus(torus):
    for row in torus:
        print(row)


MAXHORZ = 15
MAXVERT = 10
DENSITY = 50
torus = create_torus(MAXVERT, MAXHORZ, DENSITY)
pprint_torus(torus)
create_torus can be one-lier using list comprehension
def create_torus(max_vert, max_horz, density):
    return [[int(random.randint(1, 100) < density) for y in range(max_horz)] for x in range(max_vert)]
Now, because you say it's [variation of] Conway's Game of Life, I think it will be better to move to OOP/class structure sooner than later.

e.g.

import random

class Torus:
    def __init__(self, max_vert, max_horz, density):
        self._torus = [[int(random.randint(1, 100) < density) for y in range(max_horz)] for x in range(max_vert)]

    @property
    def rows(self):
        return len(self._torus)

    @property
    def columns(self):
        return len(self._torus[0])

    def __repr__(self):
        return f"Torus({self.rows} rows x {self.columns} columns: {', '.join(str(row) for row in self._torus)})"

    def __str__(self):
        return '\n'.join(str(row) for row in self._torus)

if __name__ == '__main__':
    MAXHORZ = 15
    MAXVERT = 10
    DENSITY = 50
    torus = Torus(MAXVERT, MAXHORZ, DENSITY)
    print(torus)
    print(repr(torus))
Note, I am using regular lists, not array.array, but you get the idea. You can use whatever you want. However note that you also don't use array.array (despite what you may think):
torus = array('b') # here torus is array.array
torus = [[0] * MAXHORZ] * MAXVERT # here you bind name torus to different object - list of lists, it is no longer array.array

Also note that star imports lile from array import * are generally considered bad practice and discouraged.
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#7
(Jan-21-2021, 12:41 PM)buran Wrote: what I mean:
import random

def create_torus(max_vert, max_horz, density):
    torus = []
    for x in range(max_vert):
        torus.append([])
        for y in range(max_horz):
            torus[-1].append(int(random.randint(1, 100) < density))
    return torus

def pprint_torus(torus):
    for row in torus:
        print(row)


MAXHORZ = 15
MAXVERT = 10
DENSITY = 50
torus = create_torus(MAXVERT, MAXHORZ, DENSITY)
pprint_torus(torus)
create_torus can be one-lier using list comprehension
def create_torus(max_vert, max_horz, density):
    return [[int(random.randint(1, 100) < density) for y in range(max_horz)] for x in range(max_vert)]
Now, because you say it's [variation of] Conway's Game of Life, I think it will be better to move to OOP/class structure sooner than later.

e.g.

import random

class Torus:
    def __init__(self, max_vert, max_horz, density):
        self._torus = [[int(random.randint(1, 100) < density) for y in range(max_horz)] for x in range(max_vert)]

    @property
    def rows(self):
        return len(self._torus)

    @property
    def columns(self):
        return len(self._torus[0])

    def __repr__(self):
        return f"Torus({self.rows} rows x {self.columns} columns: {', '.join(str(row) for row in self._torus)})"

    def __str__(self):
        return '\n'.join(str(row) for row in self._torus)

if __name__ == '__main__':
    MAXHORZ = 15
    MAXVERT = 10
    DENSITY = 50
    torus = Torus(MAXVERT, MAXHORZ, DENSITY)
    print(torus)
    print(repr(torus))
Note, I am using regular lists, not array.array, but you get the idea. You can use whatever you want. However note that you also don't use array.array (despite what you may think):
torus = array('b') # here torus is array.array
torus = [[0] * MAXHORZ] * MAXVERT # here you bind name torus to different object - list of lists, it is no longer array.array

Also note that star imports lile from array import * are generally considered bad practice and discouraged.

Thanks that was VERY helpful. Bye, from neighbouring Salonica (Solun)
SB
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  logger behaviour setdetnet 1 887 Apr-15-2023, 05:20 AM
Last Post: Gribouillis
  can someone explain this __del__ behaviour? rjdegraff42 1 716 Apr-12-2023, 03:25 PM
Last Post: deanhystad
  Asyncio weird behaviour vugz 2 1,250 Apr-09-2023, 01:48 AM
Last Post: vugz
  Weird behaviour using if statement in python 3.10.8 mikepy 23 3,596 Jan-18-2023, 04:51 PM
Last Post: mikepy
  Generator behaviour bla123bla 2 1,098 Jul-26-2022, 07:30 PM
Last Post: bla123bla
  Inconsistent behaviour in output - web scraping Steve 6 2,542 Sep-20-2021, 01:54 AM
Last Post: Larz60+
  Adding to the dictionary inside the for-loop - weird behaviour InputOutput007 5 2,703 Jan-21-2021, 02:21 PM
Last Post: InputOutput007
  strange behaviour- plotting nathan_Blanc_Haifa 0 1,496 Dec-27-2020, 01:37 PM
Last Post: nathan_Blanc_Haifa
  OOP behaviour problem JohnB 3 2,428 Aug-18-2020, 07:51 PM
Last Post: JohnB
  understanding basic loop behaviour vinci 5 2,877 Feb-11-2020, 09:53 PM
Last Post: vinci

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020