Search code examples
pythonctypesdynamic-arrays

ValueError: PyObject is NULL when running some test cases on the dynamic array using python?


That's my implementation of the dynamic array in python with types:

import ctypes

class DynamicArray:

  def __init__(self,):
    self.n = 0 # the current number of elements added to the array
    self.capacity = 1 # Initial capacity of the dynamic array is one 
    self.array    = self.make_array(self.capacity)

  def add_element(self,element):
    # First check whether the array is full or not
    if self.n == self.capacity:
      self.resize(2*self.capacity)
    
    self.array[self.n] = element
    self.n +=1 

    return 

  def resize(self,new_capacity):
    new_array = self.make_array(new_capacity)

    for i in range(self.n):
      new_array[i] = self.array[i]

    self.array = new_array
    self.capacity = new_capacity
    return 

  def make_array(self,capacity):
    return (capacity * ctypes.py_object)()

  def get_array(self):
    return list(self.array)

After running the following test cases:

dynamic_r = DynamicArray()
dynamic_r.add_element(4)
dynamic_r.get_array()
dynamic_r.add_element(9)
dynamic_r.get_array()
dynamic_r.add_element(10)
dynamic_r.get_array()

I get this

ValueError: PyObject is NULL

It should be noted that I don't get this error every time I run the code


Solution

  • At the point of failure in your test case, self.capacity == 4 but self.n == 3. list(self.array) converts all the elements to a list, but only the first n elements are initialized. Use list(self.array[:self.n]) to only convert the initialized elements.

    By the way, ctypes.py_object() doesn't need to be used. You could use

      def make_array(self,capacity):
        return [None] * capacity
    

    or perhaps a special sentinal object like:

    class Empty:
      def __repr__(self):
        return '<empty>'
    

    and then:

      def make_array(self,capacity):
        return [Empty()] * capacity
    

    If you print the result of dynamic_r.get_array()), you'll see the uninitialized element as None or <empty> and catch the error that way:

    [4]
    [4, 9]
    [4, 9, 10, <empty>]