Let's say you're launching a rocket. As you ascend, you need to fix the angle according to a given table. However, at certain points, you'll need to perform one-off actions (releasing boosters if they're empty, or jettisoning fairings at a certain altitude, for instance).
What I'm doing now is:
fairing_jettisoned = False
while apogee < TARGET_APOGEE:
aim_rocket(speed, altitude, apogee)
if not fairing_jettisoned and altitude > FAIRING_ALT:
jettison_fairing()
fairing_jettisoned = True
This gets very tedious with more complicated routines because you have to jump back and forth.
Other languages have a when statement (when altitude > FAIRING_ALT:), but that does not exist in Python.
One way I tried implementing it is with classes. It is hacky, but it seems to work well:
class when(object):
calls = {}
def __init__(self, expr, func, *args, runs_left=1, **kwargs):
_, fname, line, *_ = inspect.stack()[1]
stored_instance = when.calls.get((fname, line), self)
if stored_instance == self:
self.func = func
self.args = args
self.kwargs = kwargs
self.runs_left = runs_left
when.calls[(fname, line)] = self
self = stored_instance
self.expr = expr
if self.runs_left and self.expr:
self.runs_left -= 1
return self.func(*self.args, **self.kwargs
This can be run like this:
while apogee < TARGET_APOGEE:
aim_rocket(speed, altitude, apogee)
when(altitude > FAIRING_ALT, jettison_fairing)
Ultimately, what I'd like is to have this kind of syntax:
while apogee < TARGET_APOGEE:
aim_rocket(speed, altitude, apogee)
when(altitude > FAIRING_ALT, runs_left=3):
# Do a bunch of things; no need for a lambda or function.
jettison_fairing()
I also tried implementing it with __enter__ and __exit__, but I couldn't figure out a way to skip to __exit__ if it has already been run. Is such a thing possible with async/await? Or is that for something completely different?
You could try setting up something using threads, eg:
from threading import Thread
import time, random
def when(condition, action) -> Thread:
def threadtask():
while not condition():
time.sleep(0.5)
action()
t = Thread(target = threadtask)
t.start()
return t
altitude = 1
def jettison_fairing():
print("Jettison the fairing!")
when(lambda: altitude > 500, jettison_fairing)
while altitude < 1000:
time.sleep(1)
altitude += random.randint(0,300)
print("Altitude is now ",altitude)
# Output:
##Altitude is now 173
##Altitude is now 290
##Altitude is now 370
##Altitude is now 395
##Altitude is now 464
##Altitude is now 564
##Jettison the fairing!
##Altitude is now 736
##Altitude is now 762
##Altitude is now 1057
For bonus points, you could write a decorator that does that.
However. What you're describing is some of the problems faced with programming real-time systems. There's no easy "when" construct that is going to solve your problem. What does "when" mean to you--for example, how often does the program check altitude? If your program is simultaneously waiting for fifty different "when" conditions, are any of those higher priority? What happens if two "when" conditions happen at the same time, and one set of code tells the computer to jettison the fairing and the other assumes it is still there?
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