I am trying to create a simple data type to be used as a dtype for a Numpy array, and on which I can perform element wise addition, subtraction, and comparison. The type should take on (at least) three values representing true, false, and "don't care" (DC). The latter is equal to both true and false and behaves like zero in addition and subtraction:
>>> MyDtype(True) == MyDtype(DC) == MyDtype(True) # note reflection
True
>>> MyDtype(False) == MyDtype(DC) == MyDtype(False) # ditto
True
>>> MyDtype(True) == MyDtype(False)
False
>>> MyDtype(True) - MyDtype(DC) == MyDtype(True)
True
>>> MyDtype(DC) + MyDtype(False) == MyDtype(False)
True
I am totally stumped on how to get these semantics in a sane fashion.
You can use magic functions to control arithmetic operations on objects of your class. You can control even reflected operations, if the object on the left hand side does not implement the respective non-reflected operation.
A comprehensive documentation of magic methods can be found here (The link refers to the arithmetic operator section which is followed by the section about reflective arithmetic operations):
http://www.rafekettler.com/magicmethods.html#numeric
I've had the same problem and wrote a class whose objects are dontcare symbols. It is not exactly what you asked for since it does not wrap values, but it should be easy to adapt it to your needs.
You can get it here:
https://github.com/keepitfree/nicerpython
from symbols import dontcare
True == dontcare == True
>>> True
False == dontcare == False
>>> True
True == False
>>> False
True - dontcare == True
>>> True
dontcare + False == False
>>> True
Related
Let's say I have a list with different values, like this:
[1,2,3,'b', None, False, True, 7.0]
I want to iterate over it and check that every element is not in list of some forbidden values. For example, this list is [0,0.0].
When I check if False in [0,0.0] I get True. I understand that python casts False to 0 here - but how I can avoid it and make this check right - that False value is not in [0,0.0]?
To tell the difference between False and 0 you may use is to compare them. False is a singleton value and always refers to the same object. To compare all the items in a list to make sure they are not False, try:
all(x is not False for x in a_list)
BTW, Python doesn't cast anything here: Booleans are a subclass of integers, and False is literally equal to 0, no conversion required.
You would want to use is instead of == when comparing.
y = 0
print y == False # True
print y is False # False
x = False
print x == False # True
print x is False # True
Found a weird corner case on differentiating between 0 and False today. If the initial list contains the numpy version of False (numpy.bool_(False)), the is comparisons don't work, because numpy.bool_(False) is not False.
These arise all the time in comparisons that use numpy types. For example:
>>> type(numpy.array(50)<0)
<class 'numpy.bool_'>
The easiest way would be to compare using the numpy.bool_ type: (np.array(50)<0) is (np.False_). But doing that requires a numpy dependency. The solution I came up with was to do a string comparison (working as of numpy 1.18.1):
str(numpy.bool_(False)) == str(False)
So when dealing with a list, a la #kindall it would be:
all(str(x) != str(False) for x in a_list)
Note that this test also has a problem with the string 'False'. To avoid that, you could exclude against cases where the string representation was equivalent to itself (this also dodges a numpy string array). Here's some test outputs:
>>> foo = False
>>> str(foo) != foo and str(foo) == str(False)
True
>>> foo = numpy.bool_(False)
>>> str(foo) != foo and str(foo) == str(False)
True
>>> foo = 0
>>> str(foo) != foo and str(foo) == str(False)
False
>>> foo = 'False'
>>> str(foo) != foo and str(foo) == str(False)
False
>>> foo = numpy.array('False')
>>> str(foo) != foo and str(foo) == str(False)
array(False)
I am not really an expert programmer, so there may be some limitations I've still missed, or a big reason not to do this, but it allowed me to differentiate 0 and False without needing to resort to a numpy dependency.
This question already has an answer here:
Why does (1 in [1,0] == True) evaluate to False?
(1 answer)
Closed 7 years ago.
Why is it that these statements work as expected when brackets are used:
>>> (True is False) == False
True
>>> True is (False == False)
True
But it returns False when there are no brackets?
>>> True is False == False
False
Based on python documentation about operator precedence :
Note that comparisons, membership tests, and identity tests, all have the same precedence and have a left-to-right chaining feature as described in the Comparisons section.
So actually you have a chained statement like following :
>>> (True is False) and (False==False)
False
You can assume that the central object will be shared between 2 operations and other objects (False in this case).
And note that its also true for all Comparisons, including membership tests and identity tests operations which are following operands :
in, not in, is, is not, <, <=, >, >=, !=, ==
Example :
>>> 1 in [1,2] == True
False
Python has a unique transitive property when it comes to the comparison operators. It will be easier to see in a simpler case.
if 1 < x < 2:
# Do something
This does what it looks like. It checks if 1 < x and if x < 2. The same thing is happening in your non-parenthesized code.
>>> True is False == False
False
It is checking whether True is False and False == False, only one of which is true.
This is a double inequality which gets expanded as (True is False) and (False == False). See for instance What is the operator precedence when writing a double inequality in Python (explicitly in the code, and how can this be overridden for arrays?)
Python interprets multiple (in)equalities the way you would expect in Math:
In Math a = b = c mean all a = b, b = c and a = c.
So True is False == False means True == False and False == False and True == False, which is False.
For boolean constants, is is equivalent to ==.
Python performs chaining if it encounters operators of same precedence when evaluating an expression.
comparisons, including tests, which all have the same precedence
chain from left to right
The below mentioned operators have the same precedence.
in, not in, is, is not, <, <=, >, >=, <>, !=, ==
So, when Python tries to evaluate the expression True is False == False, it encounters the operators is and == which have the same precedence, so it performs chaining from left to right.
So, the expression True is False == False is actually evaluated as:
(True is False) and (False == False)
giving False as the output.
As expected, 1 is not contained by the empty tuple
>>> 1 in ()
False
but the False value returned is not equal to False
>>> 1 in () == False
False
Looking at it another way, the in operator returns a bool which is neither True nor False:
>>> type(1 in ())
<type 'bool'>
>>> 1 in () == True, 1 in () == False
(False, False)
However, normal behaviour resumes if the original expression is parenthesized
>>> (1 in ()) == False
True
or its value is stored in a variable
>>> value = 1 in ()
>>> value == False
True
This behaviour is observed in both Python 2 and Python 3.
Can you explain what is going on?
You are running into comparison operator chaining; 1 in () == False does not mean (1 in ()) == False.
Rather, comparisons are chained and the expression really means:
(1 in ()) and (() == False)
Because (1 in ()) is already false, the second half of the chained expression is ignored altogether (since False and something_else returns False whatever the value of something_else would be).
See the comparisons expressions documentation:
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
For the record, <, >, ==, >=, <=, !=, is, is not, in and not in are all comparison operators (as is the deprecated <>).
In general, don't compare against booleans; just test the expression itself. If you have to test against a boolean literal, at least use parenthesis and the is operator, True and False are singletons, just like None:
>>> (1 in ()) is False
True
This gets more confusing still when integers are involved. The Python bool type is a subclass of int1. As such, False == 0 is true, as is True == 1. You therefor can conceivably create chained operations that almost look sane:
3 > 1 == True
is true because 3 > 1 and 1 == True are both true. But the expression:
3 > 2 == True
is false, because 2 == True is false.
1 bool is a subclass of int for historic reasons; Python didn't always have a bool type and overloaded integers with boolean meaning just like C does. Making bool a subclass kept older code working.
As expected, 1 is not contained by the empty tuple
>>> 1 in ()
False
but the False value returned is not equal to False
>>> 1 in () == False
False
Looking at it another way, the in operator returns a bool which is neither True nor False:
>>> type(1 in ())
<type 'bool'>
>>> 1 in () == True, 1 in () == False
(False, False)
However, normal behaviour resumes if the original expression is parenthesized
>>> (1 in ()) == False
True
or its value is stored in a variable
>>> value = 1 in ()
>>> value == False
True
This behaviour is observed in both Python 2 and Python 3.
Can you explain what is going on?
You are running into comparison operator chaining; 1 in () == False does not mean (1 in ()) == False.
Rather, comparisons are chained and the expression really means:
(1 in ()) and (() == False)
Because (1 in ()) is already false, the second half of the chained expression is ignored altogether (since False and something_else returns False whatever the value of something_else would be).
See the comparisons expressions documentation:
Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).
For the record, <, >, ==, >=, <=, !=, is, is not, in and not in are all comparison operators (as is the deprecated <>).
In general, don't compare against booleans; just test the expression itself. If you have to test against a boolean literal, at least use parenthesis and the is operator, True and False are singletons, just like None:
>>> (1 in ()) is False
True
This gets more confusing still when integers are involved. The Python bool type is a subclass of int1. As such, False == 0 is true, as is True == 1. You therefor can conceivably create chained operations that almost look sane:
3 > 1 == True
is true because 3 > 1 and 1 == True are both true. But the expression:
3 > 2 == True
is false, because 2 == True is false.
1 bool is a subclass of int for historic reasons; Python didn't always have a bool type and overloaded integers with boolean meaning just like C does. Making bool a subclass kept older code working.
The last point of the style guide http://www.python.org/dev/peps/pep-0008
reads ...
Don't compare boolean values to True or False using ==.
Why?
Edit
Just to make it clear what im asking (and it's indicative of the problem itself), when you write
if something:
print "something is true"
You are doing an implicit conversion to a boolean which may or may not work depending on what true means. IMHO this form of programming is discouraged because of the side effects it can cause.
numberOfApples = -1
if numberOfApples:
print "you have apples" # is not what is intended.
if numberOfApples == True:
print "you have apples" # is also not what is intended.
iHaveApples = numberOfApples > 0
if iHaveApples is True: # Edit: corrected this.. the "is" is better than the ==
print "you have apples" # is correct.
Implicit conversions disguise logical errors. So why does the style guide encourage this?
It means that you should write
if greeting:
Instead of:
if greeting == True:
Similarly, you shouldn't write this either:
if (greeting == True) == True:
The extra tests are redundant and don't add any value to the code, so they should be removed.
IMHO the point of a style guide is to standardise some common constructs, in a way that makes sense, so you won't end up with wildly disparating statements that ultimately do the same thing. More, the unusual forms might suggest that the programmer had some reason to do things a different way, maybe he was trying to achieve something different than what the statement looks like.
If you want to test the truehood/falsehood of a statement, just use the statement itself or precede it by not. If you must ensure the statement evaluates to True or False (not just a truthy/falsy value), you could use statement is True - though the style guide discourages it - or maybe check its type (using isinstance). But that's usually bad design, you should avoid that unless you have a pretty good reason to do so.
Using statement == True is dangerous for many reasons: 1) only work with True, failing with other "truthy" values (like [1]); 2) might produce unexpected results if the value returned by statement redefines __eq__; 3) might produce different results if the order of the arguments is changed, etc. Note that just using the statement may also return a different value for truehood/falsehood if the value implements __nonzero__ or __len__, but for regular usage that's usually not a problem.
Some examples showing how messed up things can be if you deviate from the style:
if True: print True # True
if 1: print True # True
if [1]: print True # True
True is True # True
1 is True # False
[1] is True # False
True == True # True
1 == True # True
[1] == True # False
Edit: some more:
if 1: print True # True
if 2: print True # True
1 == True # True
2 == True # False
1 is True # False
2 is True # False
Update: as #Marcin pointed out, you can use bool to coerce a value to True/False, guaranteeing that only those values will be present. The result of that function is consistent to the default truehood/falsehood of the value (so __nonzero__ and __len__ is taken into account). Some examples:
if bool(1): print True # True
bool(1) == True # True
bool(1) is True # True
if bool(2): print True # True
bool(2) == True # True
bool(2) is True # True
1 or 2 # 1
1 and 2 # 2
bool(1) or bool(2) # True
bool(1) and bool(2) # True
bool(1) == bool(2) # True
bool(1) is bool(2) # True
Because it's redundant.
if hasChildren:
is the same as
if hasChildren == True:
but is briefer and easier to read.
In sort:
# A, Good:
if numberOfApples > 0:
print "you have apples"
# B, Also good:
iHaveApples = numberOfApples > 0
if iHaveApples:
print "you have apples"
# C, Bad:
iHaveApples = numberOfApples > 0
if iHaveApples == True:
print "you have apples"
Why would you ever pick C over A or B?
Update:
I think you're sweating bullets over some corner cases, but if those corner cases matter in your project use the appropriate comparison. In general we do know something about the type of iHaveApples, for example we know that it's the result of a comparison using >. It's reasonable, and good practice I believe, to use that information in your code. If you're asking, "what if I think it's a bool and it turns out to be an int or something else." Than I would say you have a bug in your code, you should find it, fix it, and write a test in case you make the same mistake again. Don't rely on python to find your mistakes at runtime.
I'll assert, and leave up to you to prove if you want, that if iHaveApples: behaves exactly the same, but runs faster, as if iHaveApples is True: when you know for sure that iHaveApples is a bool. Lastly I'll give an example of when is would result in undesired behavior, at least in my opinion.
>>> import numpy
>>> totalApples = numpy.sum([1, 2, 3]) == 6
>>> totalApples
True
>>> totalApples is True
False
I'll let you figure out why that doesn't work if you want (hint, check type(totalApples)).