1

The problem:

class dummy(object)
    size = 3
    empty_array = [[None for y in range(size)] for x in range(size)]

I then want my code to generate something like (formated for readability)

empty_array[ [None, None, None],
             [None, None, None],
             [None, None, None] ]

Instead, it tells me the inner 'size'(for the 'y' iteration) doesn't exist as a variable(specifically: NameError: name 'size' is not defined). This seems to imply that it can't find the outer global 'size', which is where I'm stuck.

Is there a way to generate this type of structure efficiently? Some way to keep the variable from needing to be declared in 2 separate spots?

My previous version is this, which did work fine but was a bit clunky and unintuitive for what I wanted to do.

empty_array = []
for x in range(size):
    empty_array[x] = []
    for y in range(size):
        empty_array[x].append(None)

I need 'empty' values because my use case requires (x,y) style coordinates with both empty and 'stored' values, and I'd run into list-size errors if I just tried inserting into a list of empty lists, since they don't have any actual 'positions' to lock onto.

Annoyingly, if I simply declare the inner 'size' as a number, it works fine, so I know this type of expression works in theory, but I also need to be able to define 'size' as different numbers, so a fixed-value won't work for a final solution.

Edit: Narrowed the problem down, more context: This is in a class variable declaration, which seems to cause the simple solution of [[None]*size for _ range(size)] to fail

this works:

size1 = 2
array = [[None]*size1 for x in range(size1)]

but this doesn't:

class dummy(object):
    size2 = 3
    array = [[None]*size2 for x in range(size2)]
  • I cannot replicate the NameError when I run your first code block. – rochard4u Jul 28 '23 at 14:58
  • tadman: No, the method of []*3 copies the values contained within, when I want distinct values in different rows/colums Edit: the other method, [[]*size for _ range(x)], fails to work when done in a class variable declaration rochard4u: I'm doing this as a class's variable declaration, might that impact them? – Tribune Alpha Jul 28 '23 at 15:06
  • @TribuneAlpha list comprehensions create their own scope, a function scope actually. But Class bodies *do not create enclosing scopes*, this is why you inside of a method you need to use `self` to refer to another. Same issue here, when you try to use a list comprehension – juanpa.arrivillaga Jul 28 '23 at 16:50
  • Terminology note, that is not an array. That is a list – juanpa.arrivillaga Jul 28 '23 at 16:50

2 Answers2

1

As far as I know It's not possible to reference a class variable when defining another class variable with a comprehension, because the class itself is not defined yet because list comprehensions -- as well as dict, set and generator comprehensions -- create a new function scope, and as the class body is not an enclosing scope, the variables local to the class body are not available to the created function. (Edited based on correction from @juanpa.arrivillaga)

If you really want the array to be a class variable, then just put the desired size directly in the declaration.

def dummy():  # Note, object does not have to be specified as a parent class in Python3
    size = 3
    array = [[None] * 3 for _ in range(3)]

print(dummy.array) # [[None, None, None], [None, None, None], [None, None, None]]
test = dummy()
print(test.array) # [[None, None, None], [None, None, None], [None, None, None]]

You haven't clarified your actual use case, but if you can use array as an instance variable (which is more typical) then you can reference the class.size variable within the instance methods as self.size

def dummy():  # Note, object does not have to be specified as a parent class in Python3
    size = 3
    
    def __init__(self):
        self.array = [[None] * self.size for _ in range(self.size)]

test = dummy()
print(test.array) # [[None, None, None], [None, None, None], [None, None, None]]

Or if the size could be different per instance, put it in the __init__ as a parameter

def dummy():  
   
    def __init__(self, size):
        self.size = size
        self.array = [[None] * self.size for _ in range(self.size)]

test = dummy(size=3)
print(test.array) # [[None, None, None], [None, None, None], [None, None, None]]
nigh_anxiety
  • 1,428
  • 2
  • 4
  • 12
0

Based on what you've provided, your code mostly seems correct to me. Making this change seemed to work as intended:

size = 3
empty_array = [[None]*size for i in range(size)]

Hopefully this does the trick for you! :)

Sujata R
  • 51
  • 4
  • This works in plain code, but I'm using this in a class declaration(where for some reason it doesn't): I've edited the orginal post to reflect this – Tribune Alpha Jul 28 '23 at 15:21