Python - If not statement with 0.0

2019-04-18 03:32发布

I have a question regarding if not statement in Python 2.7.

I have written some code and used if not statements. In one part of the code I wrote, I refer to a function which includes an if not statement to determine whether an optional keyword has been entered.

It works fine, except when 0.0 is the keyword's value. I understand this is because 0 is one of the things that is considered 'not'. My code is probably too long to post, but this is an analogous (albeit simplified) example:

def square(x=None):
    if not x:
        print "you have not entered x"
    else:
        y=x**2
        return y

list=[1, 3, 0 ,9]
output=[]


for item in list:
    y=square(item)
    output.append(y)

print output

However, in this case I got left with:

you have not entered x
[1, 9, None, 81]    

Where as I would like to get:

[1, 9, 0, 81]

In the above example I could use a list comprehension, but assuming I wanted to use the function and get the desired output how could I do this?

One thought I had was:

def square(x=None):
    if not x and not str(x).isdigit():
        print "you have not entered x"
    else:
        y=x**2
        return y

list=[1, 3, 0 ,9]
output=[]


for item in list:
    y=square(item)
    output.append(y)

print output

This works, but seems like a bit of a clunky way of doing it. If anyone has another way that would be nice I would be very appreciative.

6条回答
Anthone
2楼-- · 2019-04-18 04:04

If you can identify a single type that all input must be compatible with, then you can convert it and handle the exception:

def square(x=None):
    try:
        return float(x)**2
    except TypeError:
        print "You did not enter a real number"
        return None # Explicit is better than implicit

This has a few advantages.

  • It enforces that whatever the user sends you is compatible with float. This can be any number, a string containing a number ('5.0'), or anything else that can be converted to a float. (float instances are returned unchanged.)
  • It's simple. There's no weird magic going on in your method. You're not breaking duck typing. You're just enforcing your requirements to make the thing work right.
  • It's not vulnerable to the gotchas involved with actually checking the type. Your callers can tell you how their custom type converts to float. Python provides the __float__ magic method, so if your users have exotic types and need to use your function, they're not sunk.
  • It's idiomatic. Experienced Python programmers will expect this kind of code; it follows the "easier to ask forgiveness than permission" principle.

An aside (maybe):

If None is an invalid value, don't make it a default value. Just force the caller to provide input:

def square(x):
    try:
        return float(x)**2
    except TypeError:
        print "You did not enter a real number"
        return None
查看更多
你好瞎i
3楼-- · 2019-04-18 04:07
if x is None:
   print 'x is None'
查看更多
乱世女痞
4楼-- · 2019-04-18 04:08

U can handle Exception here. Make code like this

def square(x):
    if not x:
        print "you have not entered x"
    else:
        try:
            int(y)=x**2
            return y
        except TypeError:
            pass


list=[1, 3, 0 ,9]
output=[]
查看更多
爱情/是我丢掉的垃圾
5楼-- · 2019-04-18 04:11

As far as I understand it you want to catch the cases where no input was given:

if x is None:
    ...

and the cases where it's not a number. With the second one you'll end up with quite some problems, because you maybe want to allow numpy-ndarrays in the future or something else, normally I would just suggest don't bother with excluding other classes but if you only want to allow int and float then use

if isinstance(x, (int, float, ...)):
    ...

the ... represent other allowed classes (maybe Decimal or Fractions).

So you could write it as:

if x is not None and isinstance(x, (int, float)):
     return x**2
else:
     print "you have not entered x"

Because None is not an int nor a float you can also omit the first condition and write:

if isinstance(x, (int, float)):
     ...
查看更多
我欲成王,谁敢阻挡
6楼-- · 2019-04-18 04:27

Since you are using None to signal "this parameter is not set", then that is exactly what you should check for using the is keyword:

def square(x=None):
    if x is None:
        print "you have not entered x"
    else:
        y=x**2
        return y

Checking for type is cumbersome and error prone since you would have to check for all possible input types, not just int.

查看更多
看我几分像从前
7楼-- · 2019-04-18 04:28

Problem

You understand it right. not 0 (and also not 0.0) returns True in Python. Simple test can be done to see this:

a = not 0
print(a)

Result: True

Thus, the problem is explained. This line:

if not x:

Must be changed to something else.


Solutions

There are couple of ways which can be done to fix the issue. I am just going to list them from what I think is the best solution down to the last possible solutions:


  1. To handle all possible valid cases.

    Since square should naturally expect a number input with the exclusion of complex number and should return an error otherwise, I think the best solution is to evaluate using if not isinstance(x, numbers.Number) or isinstance(x, numbers.Complex):

    def square(x=None):
        if not isinstance(x, numbers.Number) or isinstance(x, numbers.Complex): # this sums up every number type, with the exclusion of complex number
            print ("you have not entered x")
        else:
            y=x**2
            return y
    
    list=[1, 3, 0 ,9]
    output=[]
    
    for item in list:
        y=square(item)
        output.append(y)
    
    print (output)
    

    numbers.Number is the abstract class to check if argument x is a number (credit to Copperfield for pointing this out).

    Excerpt from Python Standard Library Documentation explains just what you need - with the exception of complex number:

    class numbers.Number

    The root of the numeric hierarchy. If you just want to check if an argument x is a number, without caring what kind, use isinstance(x, Number).

    But, you don't want the input to be complex number. So, just omit it using or isinstance(x, numbers.Complex)

    This way, you write the definition of square exactly the way you want it. This solution, I think, is the best solution by the virtue of its comprehensiveness.


  1. To handle just the data types you want to handle.

    If you have a list valid inpug data types you, you could also put up just those specific data types you want to handle. That is, you don't want to handle the cases for data types other than what you have specified. Examples:

    if not instance(x, int): #just handle int
    if not instance(x, (int, float)): #just handle int and float
    if not instance(x, (numbers.Integral, numbers.Rational)): #just handle integral and rational, not real or complex
    

    You may change/extend the condition above easily for different data types that you want to include or to excluded - according to your need. This solution, I think, is the second best by the virtue of its customization for its validity checking.

    (Code above is done in more Pythonical way, as suggested by cat)


  1. Not handling impossible cases: you know what the users would not put up as input.

    Think it more loosely, if you know - not the data types you want to handle like in the second solution - but the data types which the user would not put, then you can have looser condition check like this:

    if not isinstance(x, numbers.Number): # this is ok, because the user would not put up complex number
    

    This solution, I think, is the third best by the virtue of being one of the simplest yet powerful checking.

    The only downside of this solution is that you don't handle complex type. Therefore can only be implementing by owing to the fact that the users would not have complex number as the input.


  1. To handle input errors only for the known possible inputs which can cause the errors.

    For example, if you know that x is always int or None - and thus the only possible input error is None - then we can simply write the logic to avoid y being evaluated only when x is None like this:

    def square(x=None):
        if x is None:
            print ("you have not entered x")
        else:
           y=x**2
           return y
    
    list=[1, 3, 0 ,9]
    output=[]
    
    for item in list:
        y=square(item)
        output.append(y)
    
    print (output)
    

    This solution has the virtue of being the simplest.

    ...and yet the most dangerous for being used if you do not know exactly what the users would put up for the input. Otherwise, this solution is fine and is also the simplest.

    Your solution, I think more or less belongs to this category. You know what input the user will give and what the user will not. Thus, using this solution or your own solution:

    if not x and not str(x).isdigit():
    

    Is fine, except that the example solution is simpler


Given your case, you can use any solution above to get:

 [1, 9, 0, 81]

(Side Note: I try to format the solutions to look like "canonical solutions" for ease of reading purpose. This way, those who have the same questions and who visit this page in the future may be able to find the solutions more comprehensive and readable)

查看更多
登录 后发表回答