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]
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.