I want to define operators for a class instance within a class function, like so:
class MyClass(object):
@property
def _arithmetic_threshold(self):
return self.threshold # this will be defined somewhere
@_arithmetic_threshold.setter
def _arithmetic_threshold(self,value):
self.threshold = value
self._define_arithmetic_attributes()
def _define_arithmetic_attributes(self):
"""
Wrapper to define all arithmetic attributes (in order to allow threshold changes)
"""
self.__add__ = self._operation_wrapper(np.add,threshold=self._arithmetic_threshold)
self.__sub__ = self._operation_wrapper(np.subtract,threshold=self._arithmetic_threshold)
self.__mul__ = self._operation_wrapper(np.multiply,threshold=self._arithmetic_threshold)
self.__div__ = self._operation_wrapper(np.divide,threshold=self._arithmetic_threshold)
However, this doesn’t seem to work – I feel like I’m missing something about how the operators -,+, etc, call these functions. i.e.:
class MyClass2(object):
def __add__(self,other,threshold=None):
if threshold is not None:
return self+other
else:
# do something here involving checking a threshold....
pass
in MyClass2, the behavior of __add__ will be different. Can anyone explain how they are different, and how to make the behavior of the operator methods in MyClass similar to MyClass2?
EDIT: Just to make clear why I’m trying to do this, here’s _operation_wrapper. The class this is a method of is a “Spectrum” object, which has an X-axis and a Y-axis. The goal is to allow arithmetic on the Y axes, but only if the X axes match. However, it is acceptable for them to match to, say, 1/5th of a pixel size, so I wanted to do more that pure ‘exact’ matching.
def _operation_wrapper(operation):
"""
Perform an operation (addition, subtraction, mutiplication, division, etc.)
after checking for shape matching
"""
def ofunc(self, other):
if np.isscalar(other):
newspec = self.copy()
newspec.data = operation(newspec.data, other)
return newspec
else: # purely for readability
if self._arithmetic_threshold == 'exact':
xarrcheck = all(self.xarr == other.xarr)
else:
if self._arithmetic_threshold_units is None:
# not sure this should ever be allowed
xarrcheck = all((self.xarr-other.xarr) < self._arithmetic_threshold)
else:
xarrcheck = all((self.xarr.as_unit(self._arithmetic_threshold_units)-other.xarr.as_unit(self._arithmetic_threshold_units)) < self._arithmetic_threshold)
if self.shape == other.shape and xarrcheck:
newspec = self.copy()
newspec.data = operation(newspec.data, other.data)
return newspec
elif self.shape != other.shape:
raise ValueError("Shape mismatch in data")
elif not xarrcheck:
raise ValueError("X-axes do not match.")
return ofunc
Special methods like
__add__()are looked up on the type of the object, not on the instance. Sois roughly translated to
This means that setting
__add__on the instance does not do anything useful (except for makinga.__add__(b)work).Your example is a bit incomplete, so I can’t provide full working code. You might be able to move the code from
_define_arithmetic_attributes()to the class body and accessself.thresholdfrom insideoperation_wrapper().(Note that I don’t get the point of the
_arithmetic_thresholdproperty. Why don’t you simply accessself.thresholditself?)