Using IF, AND, OR together with EQUAL operand together in Python [duplicate]
Asked Answered
T

6

7

I'm trying to create a function where the given value (passed as a string) is checked to see if the number of digits is either 4 or 6, and that it is a number.

My first impulse was to go with this code:

def number(x):
    if (len(x) == (4 or 6)) and x.isdigit():
        print "True"
    else:
        print "False"

This code above only passes the first test below...I don't understand why it passes this but none of the other tests:

number("1234")

Only when I separate out the len() functions will it work properly.

def number(x):
    if (len(x) == 4 or len(x) == 6) and x.isdigit():
        print "True"
    else:
        print "False"


## Checks
number("1234")
number("123456")
number("abcd")
number("abcdef")
number("1")
number("a")

The above code passes all tests.

So my questions are:

  1. What's going on here?
  2. Any way to write cleaner code for this?

Thank for the help!

** Not a duplicate question because although this question has the same underlying concepts regarding boolean operators, the problem itself is different due to the usage of len(), isdigit(), and the additional question of how best to improve it (someone commented the usage of return). Definitely adds another perspective to the other question though.

Talca answered 20/7, 2016 at 17:3 Comment(2)
Hint: What does (4 or 6) evaluate to on its own? Does len(x) equal that?Viridity
Not a duplicate. Not even close. Yes, both questions are resolved by reading the same set of the manual, but not a duplicate.Marijn
S
12

It helps to examine the logic of this line:

if (len(x) == (4 or 6)):

The (4 or 6) clause contains a logical or short circuit. The value 4 is true, so it is evaluated and returned to the == relational comparison.

The way that or works is that its lefthand side is evaluated for Boolean truth, and its value is returned if true. If the lefthand side is not Boolean true, then the righthand side is evaluated and its value is returned.

Because the lefthand side of the 4 or ... is true in a Boolean sense, the righthand side is never evaluated. Python doesn't even look past 4 or. If the left-hand value were a false value (such as 0), then the right hand side of the or would be evaluated.

To see this in action, try print 4 or 6. The output will be 4.

So since the 4 is a hard-coded true value, your comparison is semantically the same as if (len(x) == 4) -- that is, since 4 is true, 6 is never evaluated.

What I suppose you really want to know is if len(x) is either 4 or 6. You could put that to code in a couple of ways:

if(len(x) == 4 or len(x) == 6 ...

if(len(x) in (4,6) ...
Sensorimotor answered 20/7, 2016 at 17:8 Comment(1)
Strangely enough in my case this is behaving differently: Here both are taken into acount > if file.endswith(".log") or file.endswith(".blg"): Here only the left side is taken into account> if file.endswith(".log" or ".blg"):View
H
7

You can use the in operator like so:

def number(x):
    if len(x) in (4, 6) and x.isdigit():
    print "True"
else:
    print "False"

where in checks for containment in a given container. Note that 4 or 6 on their own evaluate to something undesirable, which is why your first code segment fails. You can check it out on the python shell:

>>> 4 or 6
4
Helsell answered 20/7, 2016 at 17:6 Comment(0)
C
3

You probably wanted to write

if (len(x) in (4, 6)) and x.isdigit():

Instead of

if (len(x) == (4 or 6)) and x.isdigit(): 
Cliffhanger answered 20/7, 2016 at 17:7 Comment(0)
M
3

Short answer: len(x) in [4, 6] or len(x) == 4 or len(x) == 6.

"or" is a boolean, or logical choice. (4 or 6) is guaranteed to resolve to a non-zero (true) value. In this case, it resolves to 4, so your test case passes.

Marijn answered 20/7, 2016 at 17:8 Comment(1)
It clicked in my head after reading your response - thanks. For anyone else reading this - the critical mistake I did was not thinking of OR as boolean.Talca
V
1

I'll go ahead and answer

Any way to write cleaner code for this?

Yes. Return the boolean value, rather than printing a string.

def number(x): 
    return len(x) in {4, 6} and x.isdigit()

print(number("1234")) # True

Then, it is simple do use your method in a if-statement without string comparison.

Viridity answered 20/7, 2016 at 17:11 Comment(2)
Thanks. To expand off of this - if you wanted to time this both these functions, how would you do it? I tried [ Python -m timeit "import simplified; simplified.number() ] in the console...but it doesn't work. simplified.py is the name of the file that this function resides in.Talca
Not sure. Never used timeit. I want to say this is quicker, though, because there is no if-else conditionalViridity
C
0

The trouble is in your or statement.

Any value greater than one will evaluate to True when you put it in a conditional. So (4 or 6) will always resolve to true.

You could use the in statement above or you could just use two =='s:

if (len(x) == 4 or len(x) == 6) and x.isdigit()

It's a bit wordier, I find it easier to read.

Careen answered 20/7, 2016 at 17:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.