Shortening an if, elif, elif else clause

Shortening an if, elif, elif else clause



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





It's a bit hard to tell without seeing where is the "overlap" in the code. Maybe, in this case, you should post the 30 lines.
– wim
Aug 16 at 16:04






Why not put that code in a function? def f(times_hitz, times_luz): Of course this assumes the variables are defined as fasly values when the data is not avaliable (as your current code appears to assumes)
– Chris_Rands
Aug 16 at 16:05


def f(times_hitz, times_luz):





Wrap the code you can reuse in functions?
– juanpa.arrivillaga
Aug 16 at 16:05





Is the array empty, or is it None? If it is None, and if an empty array ist okay, you could use times_hitz or or times_hitz = times_hitz if times_hitz else to get an empty list as the "default".
– tobias_k
Aug 16 at 16:10



None


None


times_hitz or


times_hitz = times_hitz if times_hitz else





How does your full code relate to the if/elif construct from your question? In your code, you just exit if either of the arrays is not present and otherwise do some stuff that involves both array. What should the code do if only one is present?
– tobias_k
2 days ago


if/elif




2 Answers
2



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).


min


filter


None


None


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.


nowtime


min


default


None


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))





I've tested this about an hour after you posted this response and it worked perfectly fine. But right now, it gives the Error: ValueError: min() arg is an empty sequence, although the API does have train departures. Any idea on why this is?
– Dominik Sidler
yesterday





@DominikSidler Seems like none (or none of the first three times[0:3]) have a departure date after nowtime. Maybe trains don't run late in the evening? I have not checked the results in detail, what happens if you get the min of all the times, not just the first three? Otherwise, add default=None or similar to handle this case.
– tobias_k
yesterday


times[0:3]


nowtime


default=None





This seems to work, although I'll have to test it later in the evening. I'm wondering if there's a neat way of including the way the train departs, like in "the train to Lucerne departs in 0:05:10"? With the code on Google Cloud, I could've done it in the if time_luz < time_hitz print("Train to Lucerne departs in", diff) or if time_hitz < time_luz print("Train to Hitzkirch departs in", diff), but I can't find a good way of doing it with the current code.
– Dominik Sidler
yesterday


if time_luz < time_hitz print("Train to Lucerne departs in", diff)


if time_hitz < time_luz print("Train to Hitzkirch departs in", diff)





@DominikSidler See my update.
– tobias_k
yesterday





Oh that makes sense and, looking back, seems obvious
– Dominik Sidler
yesterday



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)





I think OP already considered this, but needs to "Copy & Paste that Code 2 times with only slight changes". How would those changes reflect in the function?
– tobias_k
Aug 16 at 16:13






Ahh, ok my bad. It is hard to say without knowing how slight the changes are. If the changes are very small, we can allow for exception handling in the function using another if loop.
– Rui Nian
Aug 16 at 16:55






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Help:Category

How can temperature be calculated given relative humidity and dew point?

I have a recursive function to validate tree graph and need a return condition