1

I've come across a strange issue in Python that I can't quite figure out. I am using nested lists (specifically, a custom list type object containing two lists, so as to allow negative index values, named "SuperList") to simulate 3D array functionality for the purposes of organizing chunk objects for a little roguelike I'm doing. So basically, list[z][y][x]. list[z] and list[z][y] should be list objects, and list[z][y][x] should be chunk objects. (originally established as None, then overwritten)

The issue is this: when the values entered are list[-2][-3][-4], everything has its own address as it should, but as soon as x increments one to -3, (so list[-2][-3][-3]), list[z][y] and list[z][y][x] are suddenly pointing to the exact same memory address. I can't figure out why. Obviously I don't want the value of a cell in a list to have the same address as the list itself, but I haven't the foggiest as to why it's happening. Here's the code in question: (It's a function that is a smaller part of a world object that has self.chunk as a contained SuperList object.)

def chunk_init(self):
        print ("World initialize")
        if self.chunk[0] == None:
            for zchunk in range(-self.z_range,self.z_range):
                    self.chunk[zchunk] = SuperList()
                    for ychunk in range(-self.y_range,self.y_range):
                        self.chunk[zchunk][ychunk] = SuperList()
                        for xchunk in range(-self.x_range,self.x_range):
                            chnk = self.generate_blank_chunk()
                            self.chunk[zchunk][ychunk][xchunk]= chnk
return

And for good measure, here's the code for the SuperList object:

class SuperList():
    '''
    List type object that allows for negative values that also allows you
    to index into unappended values without throwing an error. Undefined values
    are set to None.
    '''

    def __init__(self, list_plus=[], list_minus=[]):
        self.list_plus = list_plus
        self.list_minus = list_minus

    def __setitem__(self,key,value):
        if key < 0:
            key = abs(key)
            if len(self.list_minus) < key+1:
                for i in range((key+1)-len(self.list_minus)):
                    self.list_minus.append(None)
                self.list_minus[key]=value
            else:
                self.list_minus[key]=value
        else:
            if len(self.list_plus) < key+1:
                for i in range((key+1)-len(self.list_plus)):
                    self.list_plus.append(None)
                self.list_plus[key]=value
            else:
                self.list_plus[key]=value
        return

    def __getitem__(self,key):
        if key < 0:
            key = abs(key)
            if len(self.list_minus) < key+1:
                for i in range((key+1)-len(self.list_minus)):
                    self.list_minus.append(None)
                self.list_minus[key]=None
                value = self.list_minus[key]
            else:
                value = self.list_minus[key]
        else:
            if len(self.list_plus) < key+1:
                for i in range((key+1)-len(self.list_plus)):
                    self.list_plus.append(None)
                self.list_plus[key]=None
                value = self.list_plus[key]
            else:
                value = self.list_plus[key]
        return value

If anyone could shed any light onto why this is happening, I would be very grateful. My nested lists can't work if the cells wind up sharing the same address as the list they inhabit, as both get set to the same value and suddenly I'm referencing chunks when I should be referencing SuperLists. Thanks again.

1 Answers1

0

This is because you are defining a mutable default in arguments. This will cause the mutable lists you defined as arguments to get reused every time you create an instance of SuperList, causing the behavior you are witnessing. You can read more about this here.

def __init__(self, list_plus=[], list_minus=[]):
    self.list_plus = list_plus
    self.list_minus = list_minus

You need to change it to something like this.

def __init__(self, list_plus=None, list_minus=None):
    self.list_plus = list_plus or []
    self.list_minus = list_minus or []

This way the lists will get re-initialized every-time you create a new instance of the class.

eandersson
  • 25,781
  • 8
  • 89
  • 110
  • 1
    That worked! Thank you very much for your prompt reply. I'm going to go RTFM some more regarding mutables and immutables, because I'm definitely missing something in the nuance of them in regards to their application here. – Benjamin Miller Nov 03 '19 at 21:15