-1

I need two attributes of a class to either both be None or both be an int. There are already checks to make sure that if they are both set to something other than None, they will be ints. So at the end of the __init__ method I am calling a small function which checks if in either order, their types differ:

def both_none_or_both_something_else(a,b): 
    if a is None and b is not None:
        return False
    if b is None and a is not None:
        return False
    return True

>> both_none_or_both_something_else(5,None)  # False

>> both_none_or_both_something_else(None,3)  # False

>> both_none_or_both_something_else(5,20)  # True

>> both_none_or_both_something_else(None, None)  # True

Can this check of the two variables be condensed into a single line?

cardamom
  • 6,873
  • 11
  • 48
  • 102

5 Answers5

5

Simply compare the results of testing for None:

return (a is None) == (b is None)
deceze
  • 510,633
  • 85
  • 743
  • 889
  • This also passes the test of returning `False` for `(0, None)` and `True` for `(0, 5)` which is an edge case I hadn't thought of that some solutions may not pass.. – cardamom Apr 12 '19 at 14:30
  • It's literally just ensuring that the result of the test for `is None` is the same for both values, which is exactly what you want. Nothing more complicated or edge-casey necessary. :) – deceze Apr 12 '19 at 14:32
2

you need Logical XOR operator: if different -> false, if equal -> true

Exclusive or (XOR, EOR or EXOR) is a logical operator which results true when either of the operands are true (one is true and the other one is false) but both are not true and both are not false. In logical condition making, the simple "or" is a bit ambiguous when both operands are true.

def both_none_or_both_something_else(a,b):
    return not bool(a) != bool(b)

print (both_none_or_both_something_else(5,None))
print (both_none_or_both_something_else(None,3))
print (both_none_or_both_something_else(5,20))
print (both_none_or_both_something_else(None,None))

output:

False
False
True
True

remark:

to both be None or both be an int:

def both_none_or_both_something_else(a,b):
    return not type(a) != type(b)

print (both_none_or_both_something_else(5,None))
print (both_none_or_both_something_else(None,3))
print (both_none_or_both_something_else(5,20))
print (both_none_or_both_something_else(None,None))

output:

False
False
True
True
ncica
  • 7,015
  • 1
  • 15
  • 37
  • 1
    `False if ... else True` is always redundant and can either be omitted entirely, or replaced with a `bool(...)` or `not ...`. – deceze Apr 12 '19 at 14:17
  • 1
    Using the `bool` constructor is dangerous because the OP only wants to test for `None`, not all falsey values, which include 0. – blhsing Apr 12 '19 at 14:18
1

This probably isn't what you you're looking for - but is much clearer than any one-liner.

def both_none(a,b):
   return a is None and b is None

def both_not_none(a,b):
   return a is not None and b is not None

def both_none_or_both_something_else(a,b): 
   return both_none(a,b) or both_not_none(a,b)
rdas
  • 20,604
  • 6
  • 33
  • 46
1

You could say someting like

all(x is None for x in (a, b)) or all(x is not None for x in (a,b))

but I can't really say it's an improvement. If you need this repeatedly you might achieve a certain elegance by encapsulating these into predicates:

def all_none(*args):
    return all(x is None for x in args)

def none_none(*args):
    return all(x is not None for x in args)
tripleee
  • 175,061
  • 34
  • 275
  • 318
-1

Just do:

return type(a) == type(b) and type(a) in [int, type(None)]
Rajan
  • 1,463
  • 12
  • 26