Python - Multiple Elif Statements Being Skipped Over in If Statement - python

I'm trying to get my program to return different statements based off the hour the user inputs. If I input a number for hour between he first two statements ((hours < 6) and (hours <= 10) or (hours >= 6)), it will return the correct string but if I input anything greater than 10 for the hour, it won't return the intended string for that hour but it will keep repeating the second string.
Any help is appreciated!
Here's my program:
https://i.stack.imgur.com/uQzBi.png
def food(hours, boolean):
if boolean == "True" or boolean == "true":
if (hours < 6):
return "no food"
elif (hours <= 10) or (hours >= 6):
return "breakfast, marmalade"
elif (hours <= 15) or (hours >= 11):
return "lunch, true,dessert"
elif (hours < 22) or (hours >= 15):
return "dinner, dessert"
else:
return "no food"
else:
if (hours < 6):
return "no food"
elif (hours <= 10) or (hours >= 6):
return "breakfast,coffee"
elif (hours <= 15) or (hours >= 11):
return "lunch, false"
elif (hours < 22) or (hours >= 15):
return "dinner"
else:
return "no food"
x = food(15, "true")
print(x)

You should be using “and” instead of “or”. Anything > 10 will also be >= 6 so the second condition always matches.

Python have boolean value True and False. Their is no need to use strings 'True' or 'False'. You can also use the power of if-elif-else logic. Python executes from top to bottom, when condition is met, it breaks. Your function can be rewritten to this:
def food(hour, boolean):
'''Food
Takes in hour as int and boolean as bool
E.g. x = food(15,True)
# TODO:
Ensure that input data types are correct.
'''
if boolean:
if hour >= 22 or hour >= 0:
return 'no food'
elif hour >= 15:
return 'dinner, dessert'
elif hour >= 11:
return 'lunch, true,dessert'
elif hour >= 6:
return 'breakfast, marmalade'
else:
raise ValueError('something wrong')
else:
if hour >= 22 or hour >= 0:
return 'no food'
elif hour >= 15:
return 'dinner'
elif hour >= 11:
return 'lunch, false'
elif hour >= 6:
return 'breakfast, coffee'
else:
raise ValueError('something wrong')
x = food(15, True)
print(x)

Looks like the first elif statement is your problem. You should use and instead of or. By using or, anything >= 6 will return breakfast marmalade, not just anything between 6 and 10.

Welcome to StackOverflow! As mentioned by the other answers, using 'and' instead of 'or' would solve your issue. However, it is redundant to include more than one condition for each meal if they are all sequential as by writing:
if (hours < 6):
return "no food"
you are already saying to only output the return value if the hour input is less than 6, hence only values more than 6 would make it to the next elif statement.
Do let me know if I misunderstood something about your program's use case that required you to write the code as such!

Related

Python - assigning categories based on value range

Solved
I am trying to figure out why my solution is wrong. Made both, second is correct. I like the first better, because the intervals are more manageable and pleasing for my bad programming brain. And quite frankly, I am kind of lost as to what happens from pH 8 (Neutral) and up (solution 2). Therefore I would like to continue to work in this style for future assignment, rather than solution 2 (correct). However, solution 1 that I prefer is wrong and returns weakly though it should be strongly acidic Why is that and how can this be fixed?
Def: assigning category based on pH (2.3)
pH & category
0–2 Strongly acidic
3–5 Weakly acidic
6–8 Neutral
9–11 Weakly basic
12–14 Strongly basic
Anything else falls in pH out of range
def pH2Category(pH):
if pH < 0 or pH > 14:
category = "pH out of range"
elif (pH >=0) and (pH < 3):
category = "Strongly acidic"
elif (pH >=3) and (pH < 6):
category = "Weakly acidic"
elif (pH >=5) and (pH < 9):
category = "Neutral"
elif (pH >=9) and (pH < 12):
category = "Weakly basic"
else:
category = "Strongly basic"
return category
print(pH2Category(2.3))
def pH2Category(pH):
if pH < 0 or pH > 14:
category = "pH out of range"
elif pH < 3:
category = "Strongly acidic"
elif pH < 6:
category = "Weakly acidic"
elif pH <= 8:
category = "Neutral"
elif pH <= 11:
category = "Weakly basic"
else:
category = "Strongly basic"
return category
print(pH2Category(2.3))
If you want to stick to a pattern similar to what you know, the following should work:
def pH2Category(pH):
if pH < 0 or pH > 14:
return "pH out of range"
elif (pH >= 0) and (pH < 3):
return "Strongly acidic"
elif (pH >= 3) and (pH < 6):
return "Weakly acidic"
elif (pH >= 6) and (pH < 9):
return "Neutral"
elif (pH >= 9) and (pH < 12):
return "Weakly basic"
else:
return "Strongly basic"
print(pH2Category(2.3))
Please note the above will a) return 'pH out of range', so if you only want that printed and not returned, replace return in the first if with a print statement, and b) you had overlapping ranges in 5-6 as #quqa mentioned in the comment, so I fixed that.
There are many ways to do what you did but here is one that can help you start with python and make your life easier
# dictionary of (min_value, max_value): 'Description of Ph'
# just fill out for your needs
table = {
(0, 3): 'Strongly Acidic',
(3, 6): 'Wakly Acidic',
(6, 9): 'Neutral',
...
}
def check_ph(ph):
for ph_range, ph_description in table.items():
# for every pair that you see in table
if ph_range[0] <= ph < ph_range[1]:
# if ph is greater or eqaual than min_value
# and lesser than max_value
print(ph_description)
# return the ph description
return ph_description
# if the value is outside the table range just print it out
print('ph out of range')
If you want something that is scalable you should consider a Segment Tree (log-speed lookups).
Doing wasteful comparisons is cheap for something with only a few categories, but even with your 5 categories you could be doing 10 comparisons when 3-4 would do.
https://en.wikipedia.org/wiki/Segment_tree
You might be interested in portion, a library I wrote that provides structure and operations for intervals. It's distributed on PyPI, so you can easily install it with pip install portion.
Among other, it provides an IntervalDict class that acts like a Python dict but accepts ranges as keys.
Applied on your example:
>>> import portion as P
>>> d = P.IntervalDict()
>>> d[P.open(-P.inf, P.inf)] = 'pH out of range'
>>> d[P.closed(0, 2)] = 'Strongly acidic'
>>> d[P.closed(3, 5)] = 'Weakly acidic'
>>> ...
>>> d[1]
'Strongly acidic'
>>> d[300]
'pH out of range'

Why doesn't "correct" change to False in this if-else ladder of conditions for date-checking?

I have correct = bool() declared outside any function or loop in the beginning of the script. I define a function and have the following conditions. Here is my code.
correct = bool()
...
def part1():
global date, now, m, d, query
#getting the date
raw = input("Date: (MM/DD) ")
#splitting the date by /
m, d = raw.split("/")
#checking for invalid date
if int(m) > 12 or int(m) < 1:
print("Something's wrong with your date.1")
correct = False
#February 29
if int(m) == 2 and int(d) > 29:
print("Something's wrong with your date.2")
correct = False
if int(m) in thirties and int(d) > 30:
print("Something's wrong with your date.3")
correct = False
if int(m) in thirty_ones and int(d) > 31:
print("Something's wrong with your date.4")
correct = False
if int(d) < 1:
print("Something's wrong with your date.5")
correct = False
else:
correct = True
print(correct)
#creating string of date from numbers
if correct == True:
date = months[m] + " " + d
print("Date = ", date)
#creating query
query = months[m] + " " + d
print("Query: " + query)
If I input 01/35 (January 35, obviously an invalid date), it goes through the conditions and prints "Something's wrong with your date.4", telling me it's faulty by the condition if int(m) in thirty_ones and int(d) > 31: (thirty_ones is a list of numbers representing months which have 31 days declared along with correct = bool()). Since it printed that statement, then it would've made correct = False. However, the query is generated, even if correct = False. Why is that?
(apologies for the indentation problems in the code)
You need to use elif.
if int(m) > 12 or int(m) < 1:
print("Something's wrong with your date.1")
correct = False
#February 29
elif int(m) == 2 and int(d) > 29:
print("Something's wrong with your date.2")
correct = False
elif int(m) in thirties and int(d) > 30:
print("Something's wrong with your date.3")
correct = False
elif int(m) in thirty_ones and int(d) > 31:
print("Something's wrong with your date.4")
correct = False
elif int(d) < 1:
print("Something's wrong with your date.5")
correct = False
else:
correct = True
It is because of the following condition.
if int(d) < 1:
print("Something's wrong with your date.5")
correct = False
else:
correct = True
This code is getting executed after "if int(m) in thirty_ones and int(d) > 31" condition and it goes to else and set it to True.
You apparently didn't declare global correct, so the toplevel assignment correct = bool() gets masked by the assignment inside the function. Anyway, don't use globals, it's terrible practice.
Also, your code is quite convoluted, you can make the if-elif ladder shorter and clearer with the following.
Python allows triple conditions with <, <=, so instead of int(m) > 12 or int(m) < 1, do not 1 <= m <= 12. Python gets that right and doesn't even need parens around the not ...
all your your if...elif ladder's early-terminations give False, so just set that at the top, and override it if you get to the successful clause. Or else move check_date(m, d) into a separate predicate helper function, and call it.
Code like this:
m, d = [int(_) for _ in raw.split("/")]
# checking for invalid date
if not 1 <= m <= 12:
return False
elif m == 2 and d > 29:
print("Something's wrong with your date.2")
return False
elif m > 30 and d > 30:
print("Something's wrong with your date.3")
return = False
elif m = 31 and d > 31:
print("Something's wrong with your date.4")
return False
else:
return True
Notes:
Python allows triple conditions with <, <=, >, >=, ==
you only ever use the int() values of m and d, so convert them at the top, already. Doing that in a list comprehension saves an extra line.

How to use series. apply() to create conditional pandas series?

I am trying to create a new column in my df using numerical data from another column. I attempted using a for loop and a series of if statements to categorize the numerical data into strings that I want to now use to create the new column. The following data is from the WNBA 2010-2011 dataset about the players.
def clean(col):
for xp in col:
if xp < 1:
print('Rookie')
elif ((xp >= 1) and (xp <= 3)):
print('Little experience')
elif ((xp >= 4) and (xp <= 5)):
print('Experienced')
elif ((xp > 5) and (xp < 10)):
print('Very experienced')
elif (xp > 10):
print("Veteran")
I tried using series.apply() and series.map() but both of these return a new column called XP as follows
XP = df.Experience.apply(clean)
df['XP'] = XP
However, when I checked the dtypes it says that the newly created column is a NONETYPE object. Is this because I am using the print function in the for loop as opposed to manipulating the actual value? If so what should I do to return the string values specified?
Thanks in advance for the help.
df = pd.DataFrame({'xp':[0,2,4,6,20,'4']})
Put in a string because you had the type error.
def clean(str_xp):
xp = int(str_xp)
if xp < 1:
return('Rookie')
elif ((xp >= 1) and (xp <= 3)):
return('Little experience')
elif ((xp >= 4) and (xp <= 5)):
return('Experienced')
elif ((xp > 5) and (xp < 10)):
return('Very experienced')
elif (xp > 10):
return ("Veteran")
df['rank'] = df['xp'].apply(clean)
df returns:
xp rank
0 0 Rookie
1 2 Little experience
2 4 Experienced
3 6 Very experienced
4 20 Veteran
5 4 Experienced
That's because your function doesn't return anything (so returns None by default). You need to replace those print statements with return.
Also, you don't need to loop over the column in your function - apply does that for you in a vectorized way. Try this:
def clean(xp):
if xp < 1:
return 'Rookie'
elif ((xp >= 1) and (xp <= 3)):
return 'Little experience'
elif ((xp >= 4) and (xp <= 5)):
return 'Experienced'
elif ((xp > 5) and (xp < 10)):
return 'Very experienced')
elif (xp > 10):
return "Veteran"
df['XP'] = df.Experience.apply(clean)
Bear in mind also that the way your equalities are currently written, your function will return None if xp == 10.

"out of range" else clause causes syntax error

I want to check the value range of an input number, then print the correct "size" (small, medium or large). If the value is out of my acceptable range, then I want the else statement to print out that the number is not valid.
Minimal example for my problem:
n = int(input("number= "))
if 0 <= n < 5:
a = "small"
if 5 <= n < 10:
a = "medium"
if 10 <= n <= 20:
a = "large"
print("this number is",a)
else:
print("thats not a number from 0 to 20")
According to Google, this is a problem with indentation. I've tried multiple ways of indenting this; I can fix the syntax, but I can't get the logic correct.
Let's fix your immediate issue: you have an else with no corresponding if statement. Syntactically, this is because you have an intervening "out-dented" statement, the print, which terminates your series of ifs.
Logically, this is because you have two levels of decision: "Is this a number 0-20?", and "Within that range, how big is it?" The problem stems from writing only one level of ifs to make this decision. To keep close to your intended logic flow, write a general if on the outside, and encapsulate your small/medium/large decision and print within that branch; in the other branch, insert your "none of the above" statement:
n = int(input("number= "))
if 0 <= n <= 20:
if n < 5:
a = "small"
elif n < 10:
a = "medium"
else:
a = "large"
print("this number is", a)
else:
print("that's not a number from 0 to 20")
You should try something like
n = int(input("number= "))
if 0 <= n < 5:
a = "small"
elif 5 <= n < 10:
a = "medium"
elif 10 <= n <= 20:
a = "large"
else:
a = "not a number from 0 to 20"
print("this number is",a)
The print statement before the else statement needs to either be removed or indented to match:
a= "large"
You've syntax (indentation) error:
n = int(input("number= "))
if 0 <= n < 5:
a = "small"
if 5 <= n < 10:
a = "medium"
if 10 <= n <= 20:
a = "large"
#print("this number is",a) indentation error in this line
else:
print("thats not a number from 0 to 20")
You can use also use following code
n = int(input("number= "))
if 10 <= n <= 20:
a = "large"
print("this number is",a)
elif 5 <= n < 10:
a = "medium"
print("this number is",a)
elif 0 <= n < 5:
a = "small"
print("this number is",a)
else:
print("thats not a number from 0 to 20")
The problem is with the print statement.
It is indented on the same level as the if block and thus, the if block ends on line containing the print statement.
Thus, the else on the next line is incorrect.
To achieve what you are trying, you should do something like this:
n = int(input("number= "))
if 0 <= n < 5:
a = "small"
elif 5 <= n < 10:
a = "medium"
elif 10 <= n <= 20:
a = "large"
else:
print("not between 0 and 20")
print("The number is", a)
The print statement needs to be placed after the else block. Also, its best to use elif statements than if statements in a situation like this.
n = int(input("Enter a number between 0 and 20: "))
if 0 <= n <= 5:
a = "small."
elif n <= 10:
a = "medium."
elif n <= 20:
a = "large."
else:
a = "invalid / out of range."
print("This number is ", a)

Not sure about python function

I am only starting and getting mad over this function (it gives me the wrong outputs):
def rental_car_cost(days):
x = 40
if days < 2:
return days*x
elif days >= 3:
return days*x-20
elif days>= 7:
return days*x-50
else:
print "Please enter nr of days"
Also, how do I make sure that a number is entered for "days"?
Not sure what you are expecting, however change the order of the elif conditions:
def rental_car_cost(days):
if isinstance(days, int):
x = 40
if days < 2:
return days*x
elif days >= 7:
return days*x-50
elif days>= 3:
return days*x-20
else:
print "Please enter nr of days"
The days>= 7 and else clauses never trigger, because the earlier days >= 3 triggers on the same inputs. if/elif/else clauses are processed in order until one of them is triggered.
What you need are clauses for days < 2, days < 7 and else.
To detect non-numbers, start with
if not isinstance(days, int):
which does a type check for integers.
rental_car_cost(2) should equal 60
But, none of your if statements will match 2. 2 isn't less than 2, nor is it greater than or equal to 3, nor is it greater than or equal to 7. Follow the advise from the other two answers by larsmans and Ankit Jaiswal also, but I'm assuming 2 should match the days*x-20 part. Just change elif days >= 3: to elif days >= 2:.

Categories

Resources