[prev in list] [next in list] [prev in thread] [next in thread] 

List:       python-list
Subject:    Re: How to convert a train running program from synchronous to asynchronous?
From:       Irv Kalb <Irv () furrypants ! com>
Date:       2019-04-26 20:28:07
Message-ID: A5486ED7-83DC-40C0-AEDA-8CF13F01BD58 () furrypants ! com
[Download RAW message or body]


> On Apr 26, 2019, at 4:18 AM, Arup Rakshit <ar@zeit.io> wrote:
> 
> I have modelled which starts running once drivers and stations are assigned to it. \
> Otherwise, it doesn't run, really don't care if passengers are boarded or not at \
> this moment. :) I think this program can help me to introduce to the Python async \
> programming domain. 
> Here is my program:
> 
> # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  # train.py
> # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  
> import time
> import random
> 
> from user import User
> 
> class NotReadyToDeparture(Exception):
> pass
> 
> class Train:
> def __init__(self, name, category):
> self.name = name
> self.category = category
> self.__drivers = []
> self.__stations = []
> 
> @property
> def drivers(self):
> return self.__drivers
> 
> @drivers.setter
> def drivers(self, coachmen):
> self.__drivers = coachmen
> 
> @property
> def stations(self):
> return self.__stations
> 
> @stations.setter
> def stations(self, places):
> self.__stations = places
> 
> def next_stoppage(self):
> return self.stations[0]
> 
> def run(self):
> total_run_time = 0
> if len(self.drivers) == 0:
> raise NotReadyToDeparture('Drivers are not yet boarded')
> 
> if len(self.stations) == 0:
> raise NotReadyToDeparture('Stoppage stations are not yet scheduled')
> 
> for station in range(len(self.stations[:])):
> run_time_to_next_stoppage = random.randint(2, 6)
> time.sleep(run_time_to_next_stoppage)
> total_run_time += run_time_to_next_stoppage
> print("Train Reached at {0} in {1} seconds".format(self.stations.pop(0), \
> run_time_to_next_stoppage)) 
> print(f"Train {self.name} is reached the destination in {total_run_time} seconds.")
> 


The underlying problem with your current "run" method is that it only returns when \
the train has visited all the stations.  This happens because of your for loop and \
the call to time.sleep inside your loop.   

Instead, I would suggest that you split the functionality of your current run method \
into two methods. The "run" method would start the train going.  Then you need a \
separate method, maybe "update" that would be called continuously.  (Code below is \
completely untested)

The run method would consist of your current checks to ensure that the drivers and \
stations are set.  Then, it should set:

     self.total_run_time = 0     # making this an instance variable that will be used \
in the update method

You also need a way of getting the current (real) time so you can know when you have \
reached the next station (I'll leave the implementation of that up to you):

      self.stations.insert(0, 'start')   # add something at the beginning of your \
list that will get popped off immediate at the first call to update  \
self.time_at_next_station = <get the current time>     


The update method will be called continuously.  It should check the current time and \
see if it has reached  the time to move on to the next station, something like:

def update(): 
     currentTime = <get the current time>  
      if currentTime < self.time_at_next_station:  # in transit to the next station, \
nothing to do  return

       # Train has arrived at a station, set up for next station
       self.stations.pop(0)      # eliminate the station at the front of the list
       if len(self.stations) == 0:
            return  # reached the end of the line

        run_time_to_next_stoppage = random.randint(2, 6) # whatever time this \
                happened plus some random seconds
        self.time_at_nextStation = currentTime + run_time_to_next_stoppage     # time \
                in the future when train gets to next station
        self.total_run_time = self.total_run_time + run_time_to_next_stoppage  # add \
to total time  print('Next stoppage is at', stations[0])
      

The first time "update" is called, it will move from the 'start' to the first \
station, and calculate when the train should reach the first station.  Because update \
is called continuously, it only needs to check if the time for getting to the next \
station has been reached.  If so, then set up for the next station.

That way, your main loop becomes:

train.run()
while len(train.stations) is not 0:     
     train.update()

Then you could have another property to get the total time (and print).

Personally, I would prefer that train.update would return True or False to say if it \
was done with the whole route.  that is, it would normally return False to say it is \
not done yet.  But when it gets to the end, it would return True.  Then your main \
code could be written as:

train.run()
while True:
    done = train.update()
    if done:
         break

Like I said, totally untested, but the idea of splitting into two methods, and \
eliminating the time.sleep is a better approach.

Irv



           



-- 
https://mail.python.org/mailman/listinfo/python-list


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic