Reputation: 2574
I would like to perform checks on the contents of a numpy array every time it is set. Can python properties be used for this? My approach:
import numpy as np
class Obj():
def __init__(self):
self._np_arr = None
@property
def np_arr(self):
if self._np_arr is None:
self._np_arr = np.ones(10)
return self._np_arr
@np_arr.setter
def np_arr(self, value):
if np.sum(value)>10:
raise ValueError('Error message')
self._np_arr = value
if __name__ == '__main__':
o = Obj()
print o.np_arr
o.np_arr = np.zeros(10) # ok
o.np_arr = np.ones(10)*2 # not ok
print o.np_arr
The getter is entered when the object is still None. Once np_arr is a numpy array the getters and setters no longer work.
What am I doing wrong?
Upvotes: 3
Views: 987
Reputation: 198
There is a some naming conflict in using the '_' prefix to mean that it's private while also defining get/set properties to directly access it as o._np_arr
.
If you want it to be really private, maybe you'd rather define explicit get/setters (o.set_arr
, o.get_arr
) and invite the user not to modify it inplace.
Upvotes: 0
Reputation: 8975
First of all. This is difficult (up to, I believe impossible) to do right. The error here is quite simple, it must be a new style class. So just replace class Obj()
with class Obj(object)
.
This may not solve your problem however, since the user can still assign it in place, ie:
o.np_arr[:] = 2
You could circumvent that somewhat by using _np_arr.setflags(write=False)
, but then the user cannot use such operations at all.
Edit:
you could also subclass ndarray, and define your own __array_finalize__
to circumvent that probably. However, even then the user could do np.asarray(o.np_arr)
and modify that in place...
I guess you could provide your own methods then "simulate" an array by wroking internally on a writeable one. But I doubt there is a 100% foolproof or at least elegent method.
Upvotes: 3
Reputation: 35089
Properties only work properly on new-style classes - changing class Obj():
to class Obj(object):
gives the expected output:
$ python2 test.py
[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
Traceback (most recent call last):
File "test.py", line 22, in <module>
o.np_arr = np.ones(10) * 2
File "test.py", line 16, in np_arr
raise ValueError('Error message')
ValueError: Error message
When Obj
is an old-style class (the default in Python 2), assigning to o.np_arr
doesn't call the setter - it behaves like a regular attribute assignment, and clobbers the property.
Upvotes: 3