0

In Python, is it possible to mimic JavaScript arrays (i. e., arrays that expand automatically when values are added outside the array's range)? In JavaScript, arrays expand automatically when a value is assigned outside of an array's index, but in Python, they do not:

theArray = [None] * 5
theArray[0] = 0
print(theArray)
theArray[6] = 0 '''This line is invalid. Python arrays don't expand automatically, unlike JavaScript arrays.'''

This would be valid in JavaScript, and I'm trying to mimic it in Python:

var theArray = new Array();
theArray[0] = 0;
console.log(theArray);
theArray[6] = 0; //the array expands automatically in JavaScript, but not in Python
5
  • Is there a reason you need to fill in an index out of range? And any reason why you need it done automatically and can't do it yourself? I'm sure you're just curious, but so am I about your intent Commented Apr 6, 2013 at 0:35
  • @Ian It's because I'm trying to port a JavaScript library to Python, and I need to find a type of object in Python that acts like a JavaScript array. Commented Apr 6, 2013 at 0:46
  • Well I'm not sure you'll find something. Then again, I'm sure someone will think of something we're forgetting. But like I said, is there any reason you can't create a new function that does what you want? Instead of just saying python_list[100] = "whatever", you'd have to do insertWithAutoFill(python_list, 100, "whatever"). I just wrote a simple function that does that. Would that be a problem to use/implement? It's either that, or creating your own class that mimics a list with this special functionality as a method Commented Apr 6, 2013 at 0:52
  • @Ian That would probably be a good solution to my problem here. Is the function's source code publicly available? Commented Apr 6, 2013 at 0:53
  • Just added an answer with the code. It's not 100% correct, but I'm still testing with different scenarios and whatnot - I just drew it up real fast Commented Apr 6, 2013 at 0:59

3 Answers 3

4

If you really need you can define such structure:

class ExpandingList(list):
    def __setitem__(self, key, value):
        try:
            list.__setitem__(self, key, value)
        except IndexError:
            self.extend((key - len(self)) * [None] + [value])

>>> a = ExpandingList()
>>> a[1] = 4
>>> a
[None, 4]
>>> a[4] = 4
>>> a
[None, 4, None, None, 4]

Integrating it with other python features might be tricky (negative indexing, slicing).

Sign up to request clarification or add additional context in comments.

1 Comment

Nice. This is the stuff I wish I understood/remembered about Python. And not that it matters, but I think handling slicing would be easy! Unless you mean when the endIndex is > the length of the list.
2

zch has already given a great answer, but I'll post this to show you how to copy behaviour for the get, too

class MyList(list):
    @property
    def length(self): # for the example bracket notation
        return len(self)
    def __getitem__(self, key): # Fix GET
        try: # assume permission
            return list.__getitem__(self, key)
        except (IndexError, TypeError): # "ask forgiveness"
            pass # using pass to save indents as try was a return
        if key == 'length': # example bracket notation for length getter
            return self.length
        try: # JavaScript doesn't care about number type and accepts
            key = int(key) # strings, too, but Python req.s integers
            return list.__getitem__(self, int(key))
        except (IndexError, ValueError):
            pass
        return None
    def __setitem__(self, key, value): # Fix SET
        try:
            list.__setitem__(self, key, value)
            return value # copy return behaviour
        except (IndexError, TypeError):
            pass
        try:
            key = int(key) # force int
        except ValueError:
            return # fail quietly?
        if key < 0: # still throw for bad key
            raise IndexError()
        self.extend((key - len(self)) * [None]) # fill gap with None
        self.append(value) # append new value
        return value # copy return behaviour

>>> a = MyList()
>>> a[0] = 1
>>> a[2] = 1
>>> (a, a[3])
([1, None, 1], None)
>>> (a.length, a['length'])
(3, 3)

3 Comments

Good idea for the getter :) And the extra check in the setter!
Nice, but I would implement getter in "ask for forgiveness, not permission" manner too (except IndexError). You will get more consistent TypeErrors. Currently for a = MyList([0]) you have a[1.0] returning None and a[0.0] raising.
Thanks @zch. Changed to ask forgiveness, fixed floats & strings, added example of implementing bracket notation. I know the return does little on the setter but I just copied JavaScript.
1

I can't think of a native way this is implemented. Saying that, it wouldn't be a bad idea to just create a function that does what you expect. Instead of using my_list[index] = value, you could use something like:

def insertWithAutoFill(the_list, index, new_item):
    if index >= len(the_list):
        for i in range(len(the_list), index):
             the_list.append(None)
        the_list.append(new_item)
    else:
        the_list[index] = new_item

And you'd call it like:

my_list = [1, 2, 3]
insertWithAutoFill(my_list, 6, 23)

With the my_list now containing:

[1, 2, 3, None, None, None, 23]

You might have to adjust some things to make sure certain things are checked properly and index is being used properly. This was minorly tested and worked in specific cases, but it's not guaranteed to be perfect :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.