Shortening an if, elif, elif else clause - python

I am making a program that checks when the next train leaves. For that, it takes the departure times from a website and stores them in arrays(times_luz and times_hitz).
However, the API sometimes doesn't have any Information, so there's no data in the array which, later in the code, leads to an error, Therefore I thought this would be a good Idea:
if times_hitz and times_luz:
Code to be executed if both contain values
elif times_luz:
Code to be executed if only times_luz contains values
elif times_hitz:
Code to be executed if only times_hitz contains values
else
print("No content available")
sys.exit()
This would technically work, but the code is currently about 30 lines long so I would have to Copy & Paste that Code 2 times with only slight changes. This would lead to about 80 lines of code and would look pretty ugly. Is there any better way of doing this?
Edit:
I made a huge mistake in guessing the size of my file, it's actually 103 lines long, including comments. Therefore, I decided to upload it to google drive:
https://drive.google.com/open?id=1F5FIuAy_g7sC_2wTprqg3EF_m_JXEreL
The error that occurs when there's no data in the array is on line 44 and 48 because the 1st item in the Array times_luz/hitz doesn't exist and can therefore not be saved to a variable. This means that I have to execute some code that only checks the _luz trains if there's nothing in times_hitz and the other way around. If both contain data, I want to execute the code I have on google drive and if neither contains data, it should print an error message.
The endings _luz and _hitz stand for Lucerne and Hitzkirch, the two ways a train can go at my station
This code basically takes times from a Train API and stores them in 3 different variables. It then checks the 3 times and stores the one that will depart next in a variable. It does this for _luz and _hitz. In the end, it checks which train(_luz or _hitz) departs earlier and prints the difference between datetime.now and the train departure time
Sorry if my explanation is unclear, feel free to ask more questions in the comments

There is some massive duplication in your code. Basically, the entire code for getting the next train is the same for the two destination (and any other destinations you might add later) and should be moved to a function.
def get_next_train(params):
res =requests.get(base, params=params)
parsed_json = res.json()
#Zeiten aus parsed_json extrahieren
time_strings = [d["from"]["prognosis"]["departure"]
for d in parsed_json["connections"]]
#String, um Zeiten in time_strings nach ISO 8601 zu parsen
iso_format = "%Y-%m-%dT%H:%M:%S%z"
# Time Strings zu datetime Objekten konvertieren
times = [datetime.strptime(ts, iso_format)
for ts in time_strings if ts is not None]
# Checken, ob times leer sind
if not times:
return None # CHANGE: return None if no times found
#Zeitzone der ersten zeit in Times speichern
tz = times[0].tzinfo
#jetztige Zeit mit Zeitzone tz, Mikrosekunden löschen
nowtime = datetime.now(tz).replace(microsecond=0)
# Checken, ob Time_1 noch in der Zukunft ist. Wenn ja, diese Zeit als Time_luz speichern
time = min(t for t in times[0:3] if t > nowtime) # CHANGE: use min
return time, time - nowtime
Then, you can get the results for the two destinations, and get the min after filtering results that are None, then just check whether that min is None (the default).
res_luz = get_next_train(params_luz)
res_hitz = get_next_train(params_hitz)
res = min(filter(None, (res_luz, res_hitz)), default=None)
if res is not None:
time, diff = res
print ("Next train", time, ", in", str(diff))
else:
print("Service nicht verfügbar")
Update: It seems like late in the evening, there might be no train departing after nowtime, causing the min to raise an exception. You can fix this by providing another default like below and returning None in this case, too.
time = min((t for t in times if t > nowtime), default=None)
return (time, time - nowtime) if time is not None else None
Update: If you want to know the destination of the next train (makes sense...) you can get it from the parameters and return it alongside the time and diff.
return time, time - nowtime, params["to"]
then unpack and print it:
if res is not None:
time, diff, dest = res
print ("Next train to %s at %s (in %s)" % (dest, time, diff))

you can instead make a definition in Python instead, so you define it once. And where ever you need to use this definition again, you can simply call it. You can also make it a class once this function gets more complicated.
Your code will be this instead:
def train_time(times_hitz, time_luz):
if times_hitz and times_luz:
Code to be executed if both contain values
elif times_luz:
Code to be executed if only times_luz contains values
elif times_hitz:
Code to be executed if only times_hitz contains values
else:
print("No content available")
sys.exit()
return leave
Where the "leave" will be from your executed code to determine when the next train leaves. And whenever you need to evaluate if the train is leaving, simply do:
leave = train_time(times_hitz, time_luz)

Related

How do I run a conditional statement "only once" and every time it changes?

I might be asking a simple question. I have a python program that runs every minute. But I would like a block of code to only run once the condition changes? My code looks like this:
# def shortIndicator():
a = int(indicate_5min.value5)
b = int(indicate_10min.value10)
c = int(indicate_15min.value15)
if a + b + c == 3:
print("Trade posible!")
else:
print("Trade NOT posible!")
# This lets the processor work more than it should.
"""run_once = 0 # This lets the processor work more than it should.
while 1:
if run_once == 0:
shortIndicator()
run_once = 1"""
I've run it without using a function. But then I get an output every minute. I've tried to run it as a function, when I enable the commented code it sort of runs, but also the processing usage is more. If there perhaps a smarter way of doing this?
It's really not clear what you mean, but if you only want to print a notification when the result changes, add another variable to rembember the previous result.
def shortIndicator():
return indicate_5min.value5 and indicate_10min.value10 and indicate_15min.value15
previous = None
while True:
indicator = shortIndicator()
if previous is None or indicator != previous:
if indicator:
print("Trade possible!")
else:
print("Trade NOT possible!")
previous = indicator
# take a break so as not to query too often
time.sleep(60)
Initializing provious to None creates a third state which is only true the first time the while loop executes; by definition, the result cannot be identical to the previous result because there isn't really a previous result the first time.
Perhaps also notice the boolean shorthand inside the function, which is simpler and more idiomatic than converting each value to an int and checking their sum.
I'm guessing the time.sleep is what you were looking for to reduce the load of running this code repeatedly, though that part of the question remains really unclear.
Finally, check the spelling of possible.
If I understand it correctly, you can save previous output to a file, then read it at the beginning of program and print output only if previous output was different.

Stuff isn't appending to my list

I'm trying to create a simulation where there are two printers and I find the average wait time for each. I'm using a class for the printer and task in my program. Basically, I'm adding the wait time to each of each simulation to a list and calculating the average time. My issue is that I'm getting a division by 0 error so nothing is being appended. When I try it with 1 printer (Which is the same thing essentially) I have no issues. Here is the code I have for the second printer. I'm using a queue for this.
if printers == 2:
for currentSecond in range(numSeconds):
if newPrintTask():
task = Task(currentSecond,minSize,maxSize)
printQueue.enqueue(task)
if (not labPrinter1.busy()) and (not labPrinter2.busy()) and \
(not printQueue.is_empty()):
nexttask = printQueue.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labPrinter1.startNext(nexttask)
elif (not labPrinter1.busy()) and (labPrinter2.busy()) and \
(not printQueue.is_empty()):
nexttask = printQueue.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labPrinter1.startNext(nexttask)
elif (not labPrinter2.busy()) and (labPrinter1.busy()) and \
(not printQueue.is_empty()):
nexttask = printQueue.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labPrinter2.startNext(nexttask)
labPrinter1.tick()
labPrinter2.tick()
averageWait = sum(waitingtimes)/len(waitingtimes)
outfile.write("Average Wait %6.2f secs %3d tasks remaining." \
%(averageWait,printQueue.size()))
Any assistance would be great!
Edit: I should mention that this happens no matter the values. I could have a page range of 99-100 and a PPM of 1 yet I still get divided by 0.
I think your problem stems from an empty waitingtimes on the first iteration or so. If there is no print job in the queue, and there has never been a waiting time inserted, you are going to reach the bottom of the loop with waitingtimes==[] (empty), and then do:
sum(waitingtimes) / len(waitingtimes)
Which will be
sum([]) / len([])
Which is
0 / 0
The easiest way to deal with this would just be to check for it, or catch it:
if not waitingtimes:
averageWait = 0
else:
averageWait = sum(waitingtimes)/len(waitingtimes)
Or:
try:
averageWait = sum(waitingtimes)/len(waitingtimes)
except ZeroDivisionError:
averageWait = 0

Assigning multiple return variables, but one at a time?

I am trying to parse some data and format using nltk, but I can't seem to assign multiple returns to multiple variables over a function iteration (see def preprocess function below.) I tried rewriting my code, which usually leads to a big debug, but it seems I am hitting my head against a Python wall that is intentionally there.
def get_7text():
with open('parsed_text/Larrys Pizza & Sports Parlor_text.csv','r') as file:
reader = csv.reader(file)
dict = [row for row in reader]
file.close()
my_dict = [l[0] for l in dict]
text= my_dict[0]
new_dict=ast.literal_eval(text)
for k,v in new_dict.items():
exec(k + '=v')
return Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
def preprocess():
for day in Days:
day = str(day)
day = sent_tokenize(day)
day = [word_tokenize(s.lower()) for s in day]
day = [pos_tag(s) for s in day]
return day
#code here
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday = get_7text()
Days=[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]
Days=preprocess()
Get7text() returns 7 strings which I can successfully assign. I used to first have it return a dictionary of 7 keys, but formatting was annoying for POS tagging, etc. for NLTK.
The problem is this. Whenever I run preprocess, the program only keeps the 1st item in the list and forgets the other 6. I am trying to force the function to assign each returned output to a list of variables called Days, but to no avail. I also noticed that AFTER Days=preprocess(), Days loses all but the first element (Tuesday through Sunday are empty list of 1 string). However, Days[3] or Days[5] prints the expected data correctly.
I'm expecting there's a better method of representation out there. There are no posts online mentioning it, and it seemed like a sketchy thing to do anyway.
Whenever python sees 'return' it says, "Oh, return, you must be done with your code. I'll stop the function now." As such, it stops after the first iteration. Instead, what you should do is :
def preprocess():
retList = []
for day in Days:
day = str(day)
day = sent_tokenize(day)
day = [word_tokenize(s.lower()) for s in day]
day = [pos_tag(s) for s in day]
retList.append(day)
return (retList)
Naturally, if this doesn't work, then I missed something and we're both at a loss.

Python: Printing data only when number enters or leaves interval

Currently I'm making a script that, given a set of celestial coordinates, will tell you on the next days when that point will be visible for a specific telescope. The criteria is simple, in the Horizontal Coordinate system, altitude of the object must be between 30 and 65 degrees(Variable "crit" here represents that, but in radians).
So I have a set of parameters for the telescope called "Ant" and then, using Pyephem:
#imported ephem as ep
obj= ep.FixedBody()
obj._ra= E.ra
obj._dec= E.dec
obj._epoch = E.epoch
Ant.date = ep.now()
for d in range(days):
for i in range(24):
for j in range (60):
Ant.date += ep.minute
obj.compute(Ant)
crit= float(obj.alt)
if crit>=0.523599 and crit <=1.13446:
print "Visible at %s" %Ant.date
Which results in printing a lot of "Visible at 2016/7/11 19:41:21", 1 for every minute.
I Just want it to print something like "Enters visibility at 2016/7/11 19:41:21, leaves at 2016/7/11 23:41:00", for example.
Any Ideas will be appreciated.
Disclaimer: Sorry, not a native english speaker.
You need to keep track of whether it is already in range. So, for instance, at the beginning you'd initialize it:
is_visible = False
and your if statement might look like:
if crit>=0.523599 and crit <=1.13446:
if not is_visible:
print "Visible at %s" %Ant.date
is_visible = True
else:
if is_visible:
print "No longer visible at %s" % Ant.date
is_visible = False

Python, loop that gets a value then tests that value again

This is my first time asking here. I tried searching for an answer, but wasn't certain how to phrase what I need so I decided to ask.
I am working on a character creator for a tabletop RPG. I want to get the results for the character's previous occupation, which are on a list, then test that value again to get the occupation previous to that.
I already have a way of getting the first occupation, which is then compared with a text database, with entries such as:
Captain ,Explorer,Knight,Sergeant,
Where Captain is the first occupation and the commas mark the beginning and the end of the possible previous occupations. I have managed to get one of those randomly, but I haven't been able to make the loop then take the selected occupation and run it again. For example:
Explorer ,Cartographer,
Here's the simplified version of my code. It gets the first part right, but I'm not sure how to trigger a loop for the next.
import random
def carOld(carrera,nivPoder):
carActual=carrera
u=0
indPoder=int(nivPoder)
carAnterior=[]
commas=[]
entTemp=[]
d=open("listaCarreras.txt","r")
f=(d.readlines())
while indPoder!=0:
indPoder=indPoder-1
for line in f:
if carActual in line:
entTemp=line.split(",")
d.close
del entTemp[0]
del entTemp[-1]
print (entTemp)
carAnterior=random.choice(entTemp)
I think this. I believe based on your description that the current occupation is in the front of the list, and the previous occupations are next in the list.
str_occs = 'Occ1,Occ2,Occ3'
list_occs = str_occs.split(',')
def prev_occ(occupation, list_occs):
prev_occ_index = list_occs.index(occupation) + 1
try:
ret_val = list_occs[prev_occ_index]
except:
ret_val = "No prior occupations."
return ret_val
You can try it out here: https://repl.it/B08A

Categories