Hi, I’ve been ramping up with param and really enjoying it so far. I am building some physical models that would benefit from unit support/ dimensional analysis. (Similar to Hints on how to extend to a new Parameter Type? - #3 by zworkinspace, not exactly like Advice on using Panel/Params with the Python Pint library (for physical quantities) which produces a widget for a standard pint Quantity.)
I have tried following the param.DataFrame source code to get started. Admittedly, I am a bit above my head with this and not exactly sure how the super methods are being used in the init and validation functions.
When I define the class as-is it will check for unit conflict on parameter initialization but not on setting the value. If I uncomment the line self._validate_units(val, self.default_units)
I get an an AttributeError: 'Quantity' object has no attribute 'default_units'
. Looking at both Number and DataFrame, this seems to be the basic procedure for checking for conflicts on initialization as well as value setting… but obviously I’m missing something. If anyone has suggestions I’d greatly appreciate it, and see how far along I can get this example!
from pint import UnitRegistry
from pint import Quantity as pQuantity
from param import ClassSelector
u = UnitRegistry()
class Quantity(ClassSelector):
"""
Parameter whose value is a pint.Quantity.
"""
__slots__ = ['default_units']
def __init__(self, default=None, default_units=None, **params):#, unit=None, magnitude=None):
super(Quantity,self).__init__(pQuantity, default=default, **params)
if default and default_units:
try:
self.default = default.to(default_units)
except:
raise ValueError("Default value %s cannot be converted to default units %s" % (default, default_units))
if default_units is not None:
self.default_units = default_units
self._validate(self.default)
# self._validate(default)
def _validate_units(self, val, default_units):
if self.default_units is not None:
try:
val.to(default_units)
except:
raise ValueError("The input value %s has units that cannot be converted default units %s" % (val, default_units))
def _validate(self, val):
# super(Quantity, self)._validate(val) # is this needed?
if self.allow_None and val is None:
return
# self._validate_units(val, self.default_units)
pass
@classmethod
def serialize(cls, value):
if value is None:
return 'null'
return str(value)
@classmethod
def deserialize(cls, value):
if value == 'null':
return None
return pQuantity(value)
class TestClass(param.Parameterized):
# quantity = Quantity(default=1*u.meter, default_units="watt")# fails, nice!
quantity = Quantity(default=1*u.meter, default_units="kilometer")# works, nice!
#test = TestClass(quantity=1*u.watt) # Needs to throw error due to unit conflict...
test = TestClass(quantity=10*u.meter) # Works