Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to implement a Python `when` construct?

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?

like image 524
Jean Nassar Avatar asked Apr 28 '26 19:04

Jean Nassar


1 Answers

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?

like image 187
jgfооt Avatar answered Apr 30 '26 09:04

jgfооt