Search code examples
python-3.xpropertiessetter

How do i define a setter for a list with an index or slicing?


With the property and setter decorator I can define getter and setter functions. This is fine for primitives but how do I index a collection or a numpy array? Setting values seems to work with an index, but the setter function doesn't get called. Otherwise the print function in the minimal example would be executed.

class Data:
    def __init__(self):
        self._arr = [0, 1, 2]

    @property
    def arr(self):
        return self._arr

    @arr.setter
    def arr(self, value):
        print("new value set")  # I want this to be executed
        self._arr = value


data = Data()

print(data.arr)  # prints [0, 1, 2]
data.arr[2] = 5
print(data.arr)  # prints [0, 1, 5]

Solution

  • You need to use a secondary class that wraps the indexing/slicing functions onto that object type.

    (Credit: inspired by this answer)

    class SubData():
        def __init__(self, arr):
            self._arr = arr.copy()
    
        def __getitem__(self, index):
            print("SubData getter")
            return self._arr[index]
    
        def __setitem__(self, index, value):
            print("SubData setter")
            self._arr[index] = value
        
        def __str__(self):
            return str(self._arr)
    
    class Data():
        def __init__(self, vals):
            self._arr = SubData(vals)
    
        @property 
        def arr(self):
            print("Data getter")
            return self._arr
        
        @arr.setter
        def arr(self, value):
            print("Data setter")
            self._arr = value
    
    #--main--
            
    >a = Data([1,2,3])
    
    >a.arr[2]=5
    Data getter
    SubData setter
    
    >print(a.arr)
    Data getter
    [1, 2, 5]
    

    For more complex behavior such as a 2D array, you would need to check the instance of the index (ie, a.arr[2,5] passes a tuple to index). This you wouldn't have to implement the subclass if you used something like a numpy array which implements these indexes.