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