Patrick Cook
Patrick Cook

Reputation: 498

I cannot get my "if" command to work properly

I am trying to add two numbers, even if they contain "-" or ".", but my if command is wrong somehow, here is the code:

def add():
    print "\nAddition"
    print " "
    print "What is your first number?"
    preadd1=raw_input(prompt)
    print "What is your second number?"
    preadd2=raw_input(prompt)
    if preadd1.isdigit() and preadd2.isdigit():
        add1=int(preadd1)
        add2=int(preadd2)
        add_answer= add1+add2
        print " "
        print add_answer
        add()
    elif preadd1=="pike" or preadd2=="pike":
        pike()
    elif "-" in preadd1 or "." in preadd1 or "-" in preadd2 or "." in preadd2 and preadd1.replace("-","").isdigit() and preadd1.replace(".","").isdigit() and preadd2.replace("-","").isdigit() and preadd2.replace(".","").isdigit():
        add1=float(preadd1)
        add2=float(preadd2)
        add_answer=add1+add2
        print ""
        print add_answer
        add()
    else:
        print "\nPlease enter two numbers."
        add()
add()

when I enter a non-number like "-sf" it returns the error:

ValueError: could not convert string to float: -sf

this makes no sense to me, seeing as a made sure preadd1.replace("-","").isdigit() and preadd1.replace(".","").isdigit() and preadd2.replace("-","").isdigit() and preadd2.replace(".","").isdigit()

Please help.

Upvotes: 0

Views: 112

Answers (5)

TheSchwa
TheSchwa

Reputation: 893

As Bill said, your if statement is short-circuiting, which means as soon as the first statement in an or pair is True, the entire statement immediately evaluates to True. I feel I should also note that, according to the docs, and is evaluated before or, which could also be causing some problems. You could possibly fix this with some well-placed parentheses, but if I may propose an alternative:

#!/usr/bin/env python

def add():
    print "\nAddition\n"
    print "What is your first number?"
    prompt=""
    preadd1=raw_input(prompt)
    print "What is your second number?"
    preadd2=raw_input(prompt)
    add1=toint(preadd1)
    add2=toint(preadd2)
    add1f=tofloat(preadd1)
    add2f=tofloat(preadd2)
    if (add1 is not None) and (add2 is not None):
        add_answer=add1+add2
        print " "
        print add_answer
        add()
    elif (add1f is not None) and (add2f is not None):
        add_answer=add1+add2
        print ""
        print add_answer
        add()
    elif preadd1=="pike" or preadd2=="pike":
        pike()
    else:
        print "\nPlease enter two numbers."
        add()

def toint(x):
    try:
        return int(x)
    except ValueError:
        return None

def tofloat(x):
    try:
        return float(x)
    except ValueError:
        return None

if __name__ == "__main__":
    add()

The functions toint() and tofloat() first try to convert the input to their corresponding type. If a ValueError occurs inside the try block (meaning the input was not a valid int or float), then the except block catches the error and instead returns None.

I would also like to point out that this is not necessarily the best use of recursion, but since I assume this is just a "learning the language" type of project I left it in my code as well. It would probably be better to use a while loop instead of recursion in this case.

Edit: Code edited to include elif statements since we don't know what's in pike(). Kudos @BillLynch.

Upvotes: 0

Larry Lustig
Larry Lustig

Reputation: 51000

Your immediate problem is a failure to understand how Python is making logical sense of the conditions in your if statement. You want Python to evaluate all the ors and then move on to the ands but that's not how Python works.

What you think you wrote:

 ("-" in preadd1 or "." in preadd1 or "-" in preadd2 or "." in preadd2)
 and 
 (preadd1.replace("-","").isdigit() and preadd1.replace(".","").isdigit() 
 and preadd2.replace("-","").isdigit() and preadd2.replace(".","").isdigit())

What Python saw:

 ("-" in preadd1)
 or 
 ("." in preadd1)
 or 
 ("-" in preadd2)
 or (
 "." in preadd2 and  preadd1.replace("-","").isdigit() and preadd1.replace(".","").isdigit() 
 and preadd2.replace("-","").isdigit() and preadd2.replace(".","").isdigit()
 )

Looked at this way, you'll see that Python never read past the very first condition ("-" in preadd1) before deciding the entire condition is True and continuing with the indented block.

You can use parentheses to bind the operators correctly and get the result you want. But you should be troubled by that extensive test to determine whether the string can be converted to a float. At the very, very least it should be factored out into a function or method with a name that indicates what you're doing but it's more typical to do something like this in Python:

 try:
      fl1 = float(preadd1)
 except:
      fl1 = None

 try:
     fl2 = float(preadd2)
 except:
     fl2 = None

 if fl1 is not None and fl2 is not None:
     # Do your float logic here
 else:
     # Do your non-float logic here

Upvotes: 0

Brian Cain
Brian Cain

Reputation: 14619

Instead of trying to predict what text is appropriate for float conversion, just do it and handle the consequences.

See also: EAFP

Incomplete snippet illustrating concept:

while True:
    try:
        text = raw_input()
        val = float(text)
        break
    except ValueError as e:
        continue

Upvotes: 2

Bill Lynch
Bill Lynch

Reputation: 81996

There's way too much code in your question:

Let's reduce your code example to what you're actually concerned about:

preadd1 = "-sf"
preadd2 = "3"

if "-" in preadd1 or "." in preadd1 or "-" in preadd2 or "." in preadd2 and preadd1.replace("-","").isdigit() and preadd1.replace(".","").isdigit() and preadd2.replace("-","").isdigit() and preadd2.replace(".","").isdigit():
    print "Shouldn't get here!"

My thoughts on your problem:

Here's your expression:

if "-" in preadd1 or "." in preadd1 or "-" in preadd2 or "." in preadd2 and preadd1.replace("-","").isdigit() and preadd1.replace(".","").isdigit() and preadd2.replace("-","").isdigit() and preadd2.replace(".","").isdigit():

There's a - in your string, so that whole expression is true.

The real solution:

You should look at this question for a variety of correct ways to test if a string can be converted to a float: Checking if a string can be converted to float in Python

Upvotes: 2

phantom
phantom

Reputation: 1455

Try wrapping your raw_input()s in int(). You'll need to catch the error so that it doesn't make it to the if.

Upvotes: 0

Related Questions