I want to model a object of air, the temperature of which can be given in 3 different ways:
- if the value is known (assuming the temperature is a constant)
- if a curve of the values with different time is known (using interpolation)
- if the formula of the temperature vs time is known.
My code is below:
from script.interpolate import interp1d
from importlib import import_module
class Air1:
# case 1: temperature, which is a constant value, is given in the input.
def __init__(self, meta: dict):
self.city = meta['city']
self.temperature = meta['temperature']
class Air2(Air1):
# case 2: a curve of temperature vs time is given in meta.
# times is a list, temperatures is a list.
# so self.get_temperature is an interpolated function.
# To get a new temperature, I can use new_temp = self.get_temperature(new_time)
def __init__(self, meta: dict):
super().__init__(meta=meta)
times = meta['times']
temperatures = meta['temperatures']
self.get_temperature = interp1d(times, temperatures)
class Air3(Air1):
# case 3: the formula to calculate temperature from time is known.
# this formula is implemented in a python file called file_of_fomurla.
# so self.get_temperature is an imported function.
# To get a new temperature, I can use new_temp = self.get_temperature(new_time)
def __init__(self, meta: dict):
super().__init__(meta=meta)
file_of_formula = meta['file_of_formula']
self.get_temperature = import_module(file_of_formula + '.my_function')
One thing I have noticed in my code is that .temperature
should only be in Air1
, since Air2
and Air3
both just have a function to calculate temperature value based on time. However, like in my code, Air2
and Air3
are sub classes of Air1
, but their input meta
will not have the key 'temperature'. So this is an error.
Do you have any better way to implement the physics behind into the model. Maybe using some abstract class?
2 Answers 2
As you yourself have noted, the inheritance isn't benefiting you at all. Just write one class.
Passing meta
as a dict
is unidiomatic. Use **kwargs
or parameters with defaults instead.
Consider dropping the get_...
prefix.
Also consider passing the formula as code (e.g. a lambda) rather than referencing a module by name.
from script.interpolate import interp1d
from importlib import import_module
class Air:
def __init__(self, city, **kwargs):
self.city = city
self.temperature = (
lambda time: kwargs['temperature'] if 'temperature' in kwargs else
interp1d(kwargs['times'], kwargs['temperatures']) if 'times' in kwargs else
import_module(kwargs['file_of_formula'] + '.my_function') if 'file_of_formula' in kwargs else
kwargs['formula']
)
-
\$\begingroup\$ @MaartenFabré They are all meant to be callable, taking a
time
parameter. \$\endgroup\$200_success– 200_success2018年09月28日 14:46:44 +00:00Commented Sep 28, 2018 at 14:46 -
\$\begingroup\$ thanks for your tips. There is a small issue, in
Air1
,temperature
is a value, so it should not be a function with atime
parameter. And by the way, could you give me an example whenkwarts['formula']
is used, assuming the formula istemperature = 2 * time + 1
. Thanks again. \$\endgroup\$aura– aura2018年09月28日 15:33:25 +00:00Commented Sep 28, 2018 at 15:33 -
\$\begingroup\$ The object must behave consistently, regardless of whether the temperature is constant or varying. A constant temperature is just a special case where it returns the same value no matter what the time is. So, it's a method that ignores its
time
parameter. \$\endgroup\$200_success– 200_success2018年09月28日 17:31:33 +00:00Commented Sep 28, 2018 at 17:31 -
\$\begingroup\$ To instantiate an
Air
object with an ad hoc formula:gotham = Air('Gotham', formula=lambda t: 2 * t + 1)
. \$\endgroup\$200_success– 200_success2018年09月28日 17:32:55 +00:00Commented Sep 28, 2018 at 17:32
you can use define a private attribute _temperature
to hold the temperature from meta
, and set it to None
if it is not defined, and define a temperature
property
:
class Air1:
# case 1: temperature, which is a constant value, is given in the input.
def __init__(self, meta: dict):
self.city = meta['city']
self._temperature = meta.get('temperature', None)
@property
def temperature(self):
if self._temperature is None:
raise NotImplementedError
return self._temperature