Wrote a function to determine if a number is a positive integer and it returns True for negative numbers

StackOverflow https://stackoverflow.com/questions/21986708

Pergunta

I defined the following function to test if an input is a positive int. I plan on using eval(raw_input("...")) so that's why the try-except part is there:

def is_ok(x):           # checks if x is a positive integer
    is_int = False
    is_pos = False
    try:
        if type(eval(x)) == int:
            is_int = True
            if x > 0:
                is_pos = True
            else:
                pass
        else:
            pass

    except NameError:
        is_int = False
        print "not even a number!"

    return is_int, is_pos 

if I try to pass a positive int it would return True, True as expected. It returns False, False and the error message for a non-number.

However, for negative numbers it still returns True for checking if it's positive. For example, adding this after the function :

is_int, is_pos = is_ok("-9")

print is_int, is_pos

running prints: True True

Can't understand why that is and would love your help. Even if there are more efficient ways to accomplish this, I'd still like to understand why it produces True True. Thanks!

(This is somewhat of a followup to: Python: How do I assign 2 values I return from a function with 1 input as values outside the function?)

Foi útil?

Solução

Shorten it down a bit:

def is_ok(x):
    is_int = False
    is_pos = False
    try:
        x = float(x)
        # Check if the number is integer or not
        is_int = x.is_integer()
        if x > 0:
            is_pos = True
    except ValueError:
        print("not even a number!")

    return is_int, is_pos

To explain how this works, you can pass a string to int() to convert this to an integer. However, passing an invalid string (like foo) will raise a ValueError. We can catch that and display our "not even a number!" message to the user. Much safer than using eval().

Try not to use eval() unless you absolutely trust the input.

Removing all the variables you can also make it:

def is_ok(x):
    try:
        # Check if is an integer and greater than 0
        return float(x).is_integer(), float(x) > 0
    except ValueError:
        print("not even a number!")
        return False, False

Explanation of why your code didn't work

Your original problem was it was returning True, True for even negative numbers. The problem here is that whilst you were using type(eval(x)) == int you were then not making x an integer object. This then has an unexpected effect when you try and compare a string to an integer later on (if x > 0):

In [9]: "twenty" > 0
Out[9]: True

You can read more about this strange behaviour in this very detailed answer.

If you were to redefine x as your variable:

try:
    x = eval(x)
    if type(x) == int:

Then when you made the comparison it would behave better and return True, False for "-9".

Outras dicas

This all seems needlessly complicated, and using eval on raw_input is a security risk - the user can enter anything they like.

Instead, try something like:

def is_ok(s):
    try:
        i = int(s)
    except ValueError:
        return False, False
    else:
        return True, i >= 0

Example inputs and outputs:

>>> for s in ["foo", "9", "-9"]:
    print s, is_ok(s)


foo (False, False)
9 (True, True)
-9 (True, False)

Your error is here:

if x > 0:

Remember x is still a string, and in Python 2.x all strings will compare > 0.

>>> def is_int_positive(n):
...     try:
...         if n == 0:
...             return True, 'Positive'
...         elif max(0,int(n)):
...             return True, 'Positive'
...         else:
...             return True, 'Negative'
...     except:
...         return False, 'Error'
...
>>> for i in [1, 0, -23, 23, 'a']:
...     print i, is_int_positive(i)
...
1 (True, 'Positive')
0 (True, 'Positive')
-23 (True, 'Negative')
23 (True, 'Positive')
a (False, 'Error')
>>>

why not make it simple

def is_positive(number):
  if number > 0:
   return True
  else:
    if number <= 0:
     return False
def is_ok(x):
    try:
        return type(x)==int, int(x)>0
    except ValueError:
        return False, False

Or:

def is_ok(x):
    try:
        return type(x)==int, 'Positive' if int(x)>0 else 'Negative'
    except ValueError:
        return False, 'Neither'
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top