If, as a simplified example, I am writing a library to help people model populations I might have a class such as:
class Population:
def __init__(self, t0, initial, growth):
self.t0 = t0,
self.initial = initial
self.growth = growth
where t0 is of type datetime. Now I want to provide a method to determine the population at a given time, whether that be a datetime or a float containing the number of seconds since t0. Further, it would be reasonable for the caller to provide an array of such times (if so, I think it reasonable to assume they will all be of the same type). There are at least two ways I can see to accomplish this:
Method for each type
def at_raw(self, t):
if not isinstance(t, collections.Iterable):
t = numpy.array([t])
return self.initial*numpy.exp(self.growth*t)
def at_datetime(self, t):
if not isinstance(t, collections.Iterable):
t = [t]
dt = numpy.array([(t1-self.t0).total_seconds() for t1 in t])
return self.at_raw(dt)
Universal method
def at(self, t):
if isinstance(t, datetime):
t = (t-self.t0).total_seconds()
if isinstance(t, collections.Iterable):
if isinstance(t[0], datetime):
t = [(t1-self.t0).total_seconds() for t1 in t]
else:
t = np.array([t])
return self.initial*numpy.exp(self.growth*t)
Either would work, but I'm not sure which is more pythonic. I've seen some suggestions that type checking indicates bad design which would suggest method 1 but as this is a library intended for others to use, method 2 would probably be more useful.
Note that it is necessary to support times given as floats, even if only the library itself uses this feature, for example I might implement a method which root finds for stationary points in a more complicated model where the float representation is clearly preferable. Thanks in advance for any suggestions or advice.
Conclusion. So, in Python, nested functions have direct access to the variables and names that you define in the enclosing function. It provides a mechanism for encapsulating functions, creating helper solutions, and implementing closures and decorators.
Python functions can return multiple values. To return multiple values, you can return either a dictionary, a Python tuple, or a list to your main program.
If you need to allow multiple types, you can use typing. Union , e.g. Union[str, int] which tells the reader that a variable can be either a string or an integer. This might be useful whenever it is not possible to require a single type or you want to provide some convenience to the users of your function or class.
In Python, a list is a way to store multiple values together.
I believe you can simply stick with the Python's Duck Typing Philosophy here
def at(self, t):
def get_arr(t):
try: # Iterate over me
return [get_arr(t1)[0] for t1 in t]
except TypeError:
#Opps am not Iterable
pass
try: # you can subtract datetime object
return [(t-self.t0).total_seconds()]
except TypeError:
#Opps am not a datetime object
pass
# I am just a float
return [t]
self.initial*numpy.exp(self.growth*np.array(get_arr(t)))
Its important, how you order the cases
Specific Cases should precede generic cases.
def foo(num):
"""Convert a string implementation to
Python Object"""
try: #First check if its an Integer
return int(num)
except ValueError:
#Well not an Integer
pass
try: #Check if its a float
return float(num)
except ValueError:
pass
#Invalid Number
raise TypeError("Invalid Number Specified")
Default Case should be the terminating case
Errors should never pass silently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With