Python 3 does not support comparison between different data types.
1 < '1' will execute with:
`TypeError: '<' not supported between instances of 'float' and 'str'`
But why does 1 == '1' (or something like 156 == ['foo']) returns False?
from the docs:
The default behavior for equality comparison (== and !=) is based on
the identity of the objects. Hence, equality comparison of instances
with the same identity results in equality, and equality comparison of
instances with different identities results in inequality. A
motivation for this default behavior is the desire that all objects
should be reflexive (i.e. x is y implies x == y).
Sometimes we would like to know if two variable are the same, meaning that they refer to the same object, e.g. True is True will return True, but on the other hand "True" is True returns False, hence it makes sense that "True" == True returns False (I didn't provide the best use case for using is operator and this example will raise a SyntaxWarning in Python3.8+ but that's the main idea)
Because it makes sense to check if something is equal to something else (or is it something else) even if they are not of the same type. However, it doesn't make much sense to check which "quantity" is larger if they aren't of the same type because "quantity" may be defined in a different way for each type (in other words, the "quantity" might measure a different quality of the object).
A non-code example: an apple clearly can not be == to an orange. However, if we define the "quantity" of an apple to be its "redness", and the "quantity" of an orange to be its "taste", we can not check if an apple is > than an orange. > will try to compare different qualities of these objects.
Back to code:
It is clear that 4 is not (or is not equal to) the list [4]. But what meaning will a check like 4 > [4] have? what does it mean for an integer to be "smaller" or "larger" from a list?
Related
I understand that Python built-in types have a "truthiness" value, and the empty string is considered False, while any non-empty string is considered True.
This makes sense
I can check this using the built-in function bool.
>>> bool("")
False
>>> bool("dog")
True
I can also make use of these truthiness values when using conditionals. For example:
>>> if "dog":
... print("yes")
...
yes
This is confusing
This doesn't work with the == operator though:
>>> "dog" == True
False
>>> "dog" == False
False
Can anyone explain why == seems to act differently than a conditional?
See the truth value testing and comparisons sections of the documentation, excerpted below.
In a nutshell, most things are truthy by default, which is why bool("dog") is true. The == operator compares two objects for equality, as opposed to comparing their truthinesses, as I assume you had expected.
4.1. Truth Value Testing
Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below.
By default, an object is considered true unless its class defines
either a __bool__() method that returns False or a __len__() method
that returns zero, when called with the object.
Here are most of the built-in objects considered false:
constants defined to be false: None and False
zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
empty sequences and collections: '', (), [], {}, set(), range(0)
Operations and built-in functions that have a Boolean result always
return 0 or False for false and 1 or True for true, unless otherwise
stated. (Important exception: the Boolean operations or and and
always return one of their operands.)
4.3. Comparisons
Objects of different types, except different numeric types, never
compare equal.
...
Non-identical instances of a class normally compare as non-equal
unless the class defines the __eq__() method.
The basics
I believe your confusion might come from comparing Python to languages such as JavaScript where there is a == and a === operator. Python does not work this way.
In Python the only way to compare for equality is with == and this compares both value and type.
Thus if you compare True == "dog", then the expression is immediately False because the types bool and str are not types that can be compared.
Although, note that it does not mean that there are no types that are comparable between themselves. Examples are set and frozenset:
frozenset({1,2,3}) == {1,2,3} # True
Or simply int and float
1 == 1.0 # True
This is the behaviour for most built-in types.
The classy part
In the case where you define your own types, i.e. when you define classes, you can write the __eq__ which is called when you compare a class object to another value.
By example you could do this (which by the way was pointed out as a terrible idea in the comments, you should not inherit built-in types).
class WeirdString(str):
def __eq__(self, other):
return str(self) == str(other) or bool(self) == bool(other)
s = WeirdString("dog")
s == True # True
In the case where you do not define __eq__, then Python fall back on comparing whether the objects are the same object with is.
When you compare "dog" == True, you are also comparing the type of these objects and not just their boolean value.
Now as True has a type bool and "dog" has a type str, they are not equivalent according to the == operator, irrespective of their boolean values being equal.
Note: Both the object's type,boolean values are being checked here.
This question already has answers here:
Is there a difference between "==" and "is"?
(13 answers)
Closed 2 years ago.
Its been a couple of days since I started learning python, at which point I stumbled across the == and is. Coming from a java background I assumed == does a comparison by object id and is by value, however doing
>>> a = (1,2)
>>> b = (1,2)
>>> a is b
False
>>> a == b
True
Seems like is is equivalent of java's == and python's == is equivalent to java's equals(). Is this the right way to think about the difference between is and ==? Or is there a caveat?
'==' checks for equality,
'is' checks for identity
See also
Why does comparing strings in Python using either '==' or 'is' sometimes produce a different result?
is checks that both operands are the same object. == calls __eq__() on the left operand, passing the right. Normally this method implements equality comparison, but it is possible to write a class that uses it for other purposes (but it never should).
Note that is and == will give the same results for certain objects (string literals, integers between -1 and 256 inclusive) on some implementations, but that does not mean that the operators should be considered substitutable in those situations.
To follow up on #CRUSADER's answer:
== checks the equality of the objects, using the eq method.
is checks the actual memory location of the objects. If they are the same memory location, they test as True
As was mentioned above, the first 2**8 integers are stored in memory locations for speed, so to see whats going on use some other object or integers above 256. For instance:
In [8]: a = 1001
In [9]: b = a # this sets a pointer to a for the variable b
In [10]: a == b
Out[10]: True # of course they are equal
In [11]: a is b
Out[11]: True # and they point to the same memory location
In [12]: id(a)
Out[12]: 14125728
In [13]: id(b)
Out[13]: 14125728
In [14]: b = 1001 #this instantiates a new object in memory
In [15]: a == b
Out[15]: True
In [16]: a is b
Out[16]: False #now the memory locations are different
In [17]: id(a)
Out[17]: 14125728
In [18]: id(b)
Out[18]: 14125824
This is one of those situations where seemingly synonymous concepts might confuse newer programmers, such as I was when I first wrote this answer. You were close with your assumption based on Java, but backwards. The difference between these operators boils down to the matter of object equivalency vs. object identity, but contrary to what you assumed, == compares by value and is compares by object id. From cpython's built-in documentation (as obtained from typing help("is") at my interpreter's prompt, but also available online here):
Identity comparisons
====================
The operators "is" and "is not" test for object identity: "x is y" is
true if and only if x and y are the same object. Object identity
is determined using the "id()" function. "x is not y" yields the
inverse truth value.
To break this down a bit for less experienced programmers (or really anyone that needs a refresher), a rough definition of each concept is given as follows:
object equivalency: two references are equivalent if they have the
same effective value.
object identity: two references are identical if they refer to the same exact object, e.g. same memory location
object equivalency occurs in most of the situations that you might expect, such as if you compare 2 == 2 or [0, None, "Hello world!"] == [0, None, "Hello world!"]. For built-in types, this is usually determined based on the value of the object, but user-defined types can define their own behavior by defining the __eq__ method (though it is still advised to do so in a way that reflects the complete value of the object).
Object identity is something that can lead to equivalence, but is, on the whole, a separate matter entirely. Object identity depends strictly on whether 2 objects (or rather, 2 references) refer to the exact same object in memory, as determined by id(). Some useful notes about identical references: because they refer to the same entity in memory, they will ALWAYS (at least in cpython) have the same value and, unless __eq__ was defined unconventionally, will therefore be equivalent. This even holds if you attempt to change one of the references through an in-place operation, such as list.append() or my_object[0]=6, and care should be taken to test identity and make copies of objects that should be separate (this is one of the main purposes of is: detecting and dealing with aliases). For example:
>>> first_object = [1, 2, 3]
>>> aliased_object = first_object
>>> first_object is aliased_object
True
>>> aliased_object[0]= "this affects first_object"
>>> first_object
['this affects first_object', 2, 3]
>>> copied_object= first_object.copy() #there are other ways to do this, such as slice notation or the copy module, but this is the most simple and direct
>>> first_object is copied_object
False
>>> copied_object[2] = "this DOES NOT affect first_object"
>>> first_object
['this affects first_object', 2, 3]
>>> copied_object
['this affects first_object', 2, "this DOES NOT affect first_object"]
There are a lot of situations that can result in 2 references being aliased, but outside the assignment operator (which always creates a reference to the assigned object, as above), many of them depend on the exact implementation, e.g. not every implementation of Python will intern strings under the same circumstances or preemptively cache (I'm not sure what the proper term is in this case) the same range of integers. My installation of cpython, for instance, seems to have cached -8 on startup when this article seems to imply that's out of the normal range. Thus, even if is seems to work in your dev environment, it's better to be on the same side, avoid inconsistent behavior altogether, and use ==. is should be reserved for situations where you actually want to compare identity.
Why does 123 < list evaluate to True? Or list < 123 to False? Or list < dict to True?
More generally why do < or > not raise errors when a number is compared to a function, a list or such things? What's the logic behind this design choice?
(note that this is in Python 2.7, it might not be true in Python 3 I didn't try it yet)
In Python2, the docs say:
CPython implementation detail: Objects of different types except
numbers are ordered by their type names; objects of the same types
that don’t support proper comparison are ordered by their address.
In Python3, this wart is fixed:
The ordering comparison operators (<, <=, >=, >) raise a TypeError
exception when the operands don’t have a meaningful natural ordering.
Thus, expressions like 1 < '', 0 > None or len <= len are no longer
valid, and e.g. None < None raises TypeError instead of returning
False. A corollary is that sorting a heterogeneous list no longer
makes sense – all the elements must be comparable to each other. Note
that this does not apply to the == and != operators: objects of
different incomparable types always compare unequal to each other.
The comparison in such cases is done on the bases of type() of the objects:
for example : type(123) is 'int' and type(list) is 'list', so the string comparison of 'int'<'list' returns to be True
>>> 123<list
True
>>> type(123)<type(list)
True
I noticed I can use the == operator to compare all the native data types (integers, strings, booleans, floating point numbers etc) and also lists, tuples, sets and dictionaries which contain native data types. In these cases the == operator checks if two objects are equal. But in some other cases (trying to compare instances of classes I created) the == operator just checks if the two variables reference the same object (so in these cases the == operator is equivalent to the is operator)
My question is: When does the == operator do more than just comparing identities?
EDIT: I'm using Python 3
In Python, the == operator is implemented in terms of the magic method __eq__, which by default implements it by identity comparison. You can, however, override the method in order to provide your own concept of object equality. Note, that if you do so, you will usually also override at least __ne__ (which implements the != operator) and __hash__, which computes a hash code for the instance.
I found it very helpful, even in Python, to make my __eq__ implementations comply with the rules set out in the Java language for implementations of the equals method, namely:
It is reflexive: for any non-null reference value x, x.equals(x) should return true.
It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
For any non-null reference value x, x.equals(null) should return false.
the last one should probably replace null with None, but the rules are not as easy here in Python as in Java.
== and is are always conceptually distinct: the former delegates to the left-hand object's __eq__ [1], the latter always checks identity, without any delegation. What seems to be confusing you is that object.__eq__ (which gets inherited by default by user-coded classes that don't override it, of course!) is implemented in terms of identity (after all, a bare object has absolutely nothing to check except its identity, so what else could it possibly do?!-).
[1] omitting for simplicity the legacy concept of the __cmp__ method, which is just a marginal complication and changes nothing important in the paragraph's gist;-).
The == does more than comparing identity when ints are involved. It doesn't just check that the two ints are the same object; it actually ensures their values match. Consider:
>>> x=10000
>>> y=10000
>>> x==y,x is y
(True, False)
>>> del x
>>> del y
>>> x=10000
>>> y=x
>>> x==y,x is y
(True, True)
The "standard" Python implementation does some stuff behind the scenes for small ints, so when testing with small values you may get something different. Compare this to the equivalent 10000 case:
>>> del y
>>> del x
>>> x=1
>>> y=1
>>> x==y,x is y
(True, True)
What is maybe most important point is that recommendation is to always use:
if myvalue is None:
not
if myvalue == None:
And never to use:
if myvalue is True:
but use:
if myvalue:
This later point is not so supper clear to me as I think there is times to separate the boolean True from other True values like "Alex Martelli" , say there is not False in "Alex Martelli" (absolutely not, it even raises exception :) ) but there is '' in "Alex Martelli" (as is in any other string).
This question already has answers here:
Why does comparing strings using either '==' or 'is' sometimes produce a different result?
(15 answers)
Closed 9 years ago.
I noticed a Python script I was writing was acting squirrelly, and traced it to an infinite loop, where the loop condition was while line is not ''. Running through it in the debugger, it turned out that line was in fact ''. When I changed it to !='' rather than is not '', it worked fine.
Also, is it generally considered better to just use '==' by default, even when comparing int or Boolean values? I've always liked to use 'is' because I find it more aesthetically pleasing and pythonic (which is how I fell into this trap...), but I wonder if it's intended to just be reserved for when you care about finding two objects with the same id.
For all built-in Python objects (like
strings, lists, dicts, functions,
etc.), if x is y, then x==y is also
True.
Not always. NaN is a counterexample. But usually, identity (is) implies equality (==). The converse is not true: Two distinct objects can have the same value.
Also, is it generally considered better to just use '==' by default, even
when comparing int or Boolean values?
You use == when comparing values and is when comparing identities.
When comparing ints (or immutable types in general), you pretty much always want the former. There's an optimization that allows small integers to be compared with is, but don't rely on it.
For boolean values, you shouldn't be doing comparisons at all. Instead of:
if x == True:
# do something
write:
if x:
# do something
For comparing against None, is None is preferred over == None.
I've always liked to use 'is' because
I find it more aesthetically pleasing
and pythonic (which is how I fell into
this trap...), but I wonder if it's
intended to just be reserved for when
you care about finding two objects
with the same id.
Yes, that's exactly what it's for.
I would like to show a little example on how is and == are involved in immutable types. Try that:
a = 19998989890
b = 19998989889 +1
>>> a is b
False
>>> a == b
True
is compares two objects in memory, == compares their values. For example, you can see that small integers are cached by Python:
c = 1
b = 1
>>> b is c
True
You should use == when comparing values and is when comparing identities. (Also, from an English point of view, "equals" is different from "is".)
The logic is not flawed. The statement
if x is y then x==y is also True
should never be read to mean
if x==y then x is y
It is a logical error on the part of the reader to assume that the converse of a logic statement is true. See http://en.wikipedia.org/wiki/Converse_(logic)
See This question
Your logic in reading
For all built-in Python objects (like
strings, lists, dicts, functions,
etc.), if x is y, then x==y is also
True.
is slightly flawed.
If is applies then == will be True, but it does NOT apply in reverse. == may yield True while is yields False.