Dynamic Array implementation in Python I am trying to learn data structures by taking this Udemy Course, Easy to Advanced Data Structures by William Fiset. The course provided an implementation of Dynamic Array in Java. To ensure I fully understood the material, I tried to re-implement it in Python. Have provided the code below. Please let me know if I am doing it right or if there is anything wrong with it. Much thanks for your help!

"""
A generic dynamic array implementation

"""
class DynamicArray:
    def __init__(self, capacity=0):
        self._index = 0
        self.capacity = capacity # actual array size
        self.arr = [None for _ in range(self.capacity)] 
        self.size = 0 # length user thinks array is

    def __len__(self):
        return self.size

    def isEmpty(self):
        return self.size == 0

    def __getitem__(self, index):
        return self.arr[index]

    def __setitem__(self, index, elem):
        self.arr[index] = elem

    def clear(self):
        for i in range(self.size): self.arr[i] = None

    def add(self, elem):
        # To resize
        if self.size + 1 &gt;= self.capacity:
            if self.capacity == 0: 
                self.capacity = 1
            else: 
                self.capacity *= 2
            new_arr = DynamicArray(self.capacity)
            for i in range(self.size):
                new_arr[i] = self.arr[i]
            self.arr = new_arr
        self.arr[self.size] = elem
        self.size += 1

    # Removes an element at the specified index in this array
    def removeAt(self, rm_index):
        if rm_index &gt;= self.size or rm_index &lt; 0: 
            raise IndexError 
        data = self.arr[rm_index]
        new_arr = DynamicArray(self.size - 1)
        i, j = 0, 0
        while i &lt; self.size: #self.size = 3 
            if i == rm_index: 
                j -= 1
            else: 
                new_arr[j] = self.arr[i]
            i += 1
            j += 1
        self.arr = new_arr
        self.size -= 1
        return data

    def remove(self, elem):
        index = self.indexOf(elem)
        if index == -1: return False
        self.removeAt(index)
        return True

    def indexOf(self, elem):
        for i in range(self.size):
            if elem == self.arr[i]:
                return i
        return -1

    def __contains__(self, elem):
        return self.indexOf(elem) != -1



    def __iter__(self):
        return self

    def __next__(self):
        if self._index &gt; self.size: raise StopIteration
        else:
            data = self.arr[self._index]
            self._index += 1
            return data

    def __str__(self):
        if self.size == 0: return "[]"
        else:
            ans = "["
            for i in range(self.size - 1):
                ans += str(self.arr[i]) + ", "
            ans += str(self.arr[self.size - 1]) + "]"
        return ans
Your code looks good, but there are four things I would improve:

Style

Overall your code follows the PEP 8 Style Guide, but:


Names should use snake_case, so index_of instead of indexOf, etc
Comments after code should leave 2 white spaces after the code:


self.size = 0 # length user thinks array is  &lt;- wrong
self.size = 0  # length user thinks array is &lt;- correct


I don't know if this is just my preference, but I think it's better to group the public methods like is_empty, index_of etc and group the overloads like __getitem__, __setitem__

Clear

At least for me, what I would expect of a method called clear is that it removes all objects, leaving the array empty. So in my opinion your clear method should just set self.size = 0. You don't need to set the elements to null because they don't matter anymore.

Is empty?

In Python, you can check if a list contains any elements by doing:

if my_list:


I think users would expect the same behaviour for your class, which you can implement with the __bool__ (Python 3.x) or __nonzero__ (Python 2.x) methods. Just return not is_empty()

Iterator

The biggest flaw I see in the code is your implementation of iteration. You are keeping the index in the array object; this means that the user cannot do:

for x in my_array:
    for y in my_array:


Because the _index is shared in both loops.

You can solve this by implementing the iterator in a different class. I would declare it as a nested class, starting with an underscore to indicate the user that it should be considered private:

class DynamicArray:
    class _Iterator:
        def __init__(self, dynamic_array):
             # ....
        # Implement '_index' and '__next__' in this class

    def __iter__(self):
        # Return a different object every time you are requested an iterator
        return _Iterator(self)