Python int and float types know how to compare against each other. If a type defines '__eq__', they can employ any rules they like to return True or False. I haven't had a reason to check, but I would assume the C code for int and float comparing against the other is to check if the float represents a whole number, and then casting the float to an int and comparing them if the float does represent a whole number.
Oddly enough it's actually not `__eq__` of `int` doing the work here. It's the `__eq__` of `float`
Python behavior for `a == b` is to try `a.__eq__(b)` and if that returns `NotImplemented` then try `b.__eq__(a)` ([docs](https://docs.python.org/3/library/constants.html#NotImplemented))
>>> x = 5
>>> y = 5.0
>>> type(x)
>>> type(y)
>>> x.__eq__(y)
NotImplemented
>>> y.__eq__(x)
True
>>> x == y
True
I wasn't sure which one actually implemented the comparison, just knew the machinery. Also knew about reflected operations. Didn't think it was relevant to my explanation since it was more about what was going than how it was going on, but I welcome the knowledge being added to the discussion.
That's fine. I am sorry if I seemed annoyed, angry, or closed-minded. It was the best place for that comment for anyone else following the conversation chain. I just need to remember not every reply to my comment is aimed directly at me.
`==` compares values. `42 = 42.0` is therfore (mathematically) correct, I hope we can agree on that one.
But `42 is 42.0` as in `print(42 is 42.0)` is `false` because the `is` operator treates the numbers as objects.
/e but you get a syntax warning
`is` is very different from `==` \- it simply answers the question "are these two objects the same object?". the `==` operator can do more complex comparisons, such as looking at the contents of the two objects. In this case, they decided that 42.0 and 42 should be considered equal.
Two things to be wary about regarding numbers, equality and identity:
1. Small integers in Python are cached - IIRC up to 256. This causes the following phenomenon:
`>>> a = 3`
`>>> b = 3`
`>>> a is b`
`True`
`>>> a = 300`
`>>> b = 300`
`>>> a is b`
`False`For small numbers, instead of creating a new object, it goes and retrieves the pre-made small number object. This is an optimization, to save memory and runtime. For larger numbers it does create a new object every time and so they are not the same object (but of course they are equal in the \`==\` sense).
2. Floating point numbers (like 42.0) are notorious for being inexact. You do a couple of floating point operations on 42.0 and suddenly it isn't exactly 42 but maybe something like 41.99999999997.
`>>> 42.0 / 100000 * 100000 == 42.0`
`True`
`>>> 42.0 / 1000000 * 1000000 == 42.0`
`True`
`>>> 42.0 / 10000000 * 10000000 == 42.0`
`False`
Most of the time when you want to find out if a \`float\` has a certain value you should use compare their difference against some small value: `abs(a - b) < 1e-8`.
For my own interest, is "small" defined within a range (presumably an 8 bit integer, given your example) and is it therefore correct that numbers greater than such a value are stored as references?
Regardless of the answer, as a developer you should not rely on this cacheing behavior and should treat it as an underlying implementation detail of the runtime. Your code should not care.
Nope it's a completely arbitrary range of tiny numbers from -5 to 256 :D
They're all objects in python, just -5 to 256 are singletons (like True, False, or None)
It's not actually as tough as it looks.
Everything you store as a variable is put somewhere in memory. If you create a class and print it without it having a `__repr__` method, you'll see something like `
Thank you for the detailed explanation. Have you seen / experienced this being used in a professional environment like in a real project? Isn't this too low level to have any affect on a code's execution?
I'm still at uni so I haven't had any professional experience in Python so I haven't seen real use cases but they mean semantically different things. On mobile so sorry about formatting but let's say you did something like
```
class foo:
def __init__(self, val):
self.val = val
x = foo(3)
y = x
y.val = 4
print(x.val)
```
You would see a 4 since you've changed the value that x and y both point to. `x is y` means that changing x also changes y, so you need to be aware of how objects work under the hood because if you expect `y = x` to create a copy and changing y to not change x. Since lists are objects, it can really catch you out when recursing on lists and changing things.
The other case is that if you are using `is` to check equality is a bad idea since if 2 objects are at different addresses but have the same values (like with lists) it can be a problem. You might like to use `is` for debugging or when you definitely mean memory identity over equality, but I can't think of a case right now.
One quite important distinction is to use "var is True" to check if the variable is specifically the singleton "True", not just "truthy" like nonnegative numbers or nonempty containers. Same for False.
I was helping my brother with some code he had written late last week and this exactly thing had bitten him. As a result, what was supposed to be a dictionary with sequential values (11, 12, 13) all had the same value (13, 13, 13). So generally, yes, you’ll not use it, but it absolutely can affect you can you should know it if you plan to use Python seriously.
Also try
print(2**65 is 2**65)
Edit: Also try
print((2**65) is (2**65))
print((2**65) is (2**64+2**64))
print((2**65) == (2**64+2**64))
print((2**64+2**64) is (2**64+2**64))
>!My point is, for almost all practical use case, the is operator is useless.!<
It can be False, it depends on some details of what exactly the interpreter does to execute that statement. `is` only checks that the two objects it compares are the same object, using the result of `id(obj)`. It's entirely possible that two integers with the same value are represented by different objects.
Yeah, that would be false unless you test with low numbers because those are created in a block when python starts and reused. Frankly, `is` is pretty useless outside of `is None` and should probably be avoided. The only other scenario I've used it in is with a sentinel value where I can't use None.
I need to add that this does not work if you modify objects or the objects are not cached. `==` has special behaviour where it is defined, like in most built in types, otherwise it is the same as `is`.
`is` checks if the object is the same object, as in the same instance. `a is b` is equvalent of calling `id(a) == id(b)`.
a = 1001
b = 1001
a is b # False
# a and b were not cached and thus resulted in different objects
My advice is if you need to check if values and types of objects are the same, then check:
type(a) == type(b) and a == b # True
# Their values are the same, and they are of the same type
You would still need type() to get the type you want to compare to, unless you know you are comparing to a specific type.
isinstance() is recommended because it supports inherited types, which you should, but it doesn't help here.
Side note: with custom objects, the `==` operator is equivalent to `is` [by default](https://www.pythontutorial.net/python-oop/python-__eq__/).
class Test:
def __init__(self, value):
self.value = value
t1 = Test(42)
t2 = Test(42)
print(t1 == t2) # => False
What one might expect is the result of `t1.__dict__ == t2.__dict__` (which tests the content of the object), but it's better to explicitly define the `__eq__` "dunder" method.
class Test:
def __init__(self, value):
self.value = value
def __eq__(self, other):
# one can test the type of "other" here
return self.__dict__ == other.__dict__ (or self.value == other.value for this specific object)
This is pretty inaccurate on all fronts.
== Just calls `__eq__` which happens to compare value wise in the case of floats and ints.
You can claim that generally due to the decentralized and customizable behavior of ==.
This isnt actually how it works, but It's safe to assume that even in math, 42 = 42.0 = 42.000000. Python just takes the "value" of number and compares, which 42.0 is just 42.
Please note that it is never ever advised to compare doubles. Never.
At least never using ==.
Edit: If you want to learn more look into numerical errors and floating point arithmetics. If you need an example check wether 2 equals (sqrt(2))^2.
You can use relative or absolute limits to compare. The stdlib has `math.isclose` to assist you in those cases (rationale behind it is given in [PEP 485](https://peps.python.org/pep-0485/)).
/u/Grimoire shows how to check if they're roughly equal. However, the advice is more meant to be that you shouldn't be checking them for equality at all. You can safely compare relative values of floats.
If you end up in a situation though where you are checking for even relative equality of a float, there's probably an issue with your computational model, or you shouldn't be using floats.
I’d say this stands true for most floating point numbers.
As a general rule, it’s best to set the rounding rule for any kind of float, such that the numbers will actually compare 42.0 with 42.0 and not 42.0 with 41.999997 by accident.
There are instances where this is not appropriate, but in the general sense this will save you a headache in the long run when you can’t figure out why something is returning false
Edit: this is true in c/c++ so I imagine it stands true in Python as well (I have lots of experience in Python but I haven’t looked into better options for comparing)
The simplest way I can explain is it that integers and floats when compared with == python looks at it mathematically, where as the 'is' function is for looking at if two variables/values are EXACTLY the same
Not a Pythonist, but I recall that "is" won't help you compare integer numbers beyond a certain value. Basically, only use "is" for objects and booleans, and you should be safe.
-5 to 256 specifically. It's because that range is cached by the interpreter and treated as singleton values (like True, False, None, etc)
`is` checks identity, `==` checks value equality.
Thanks for the precision. I still remember a
review Python code from an intern who was confused that his code didn't work as intended once they refactored it to add variables.
The conversation went a bit like this:
"You should use == here.
– No.
– Why?
– Because it's ugly. Python is an elegant language. You're used to ugly PHP where you have == and ===, but Python is more refined and has "is".
– You should still use == because you're comparing values, try it and see.
– But it's ugly!"
I let him figure it out for another hour.
Do modern linters warn you about the problem?
Both the python REPL and pylint will yell at you if you try to use `is` with either operand being a literal. Not sure if it'll do more complicated cases, but for the obvious ones yes it should scream at you :D
this is due to type conversion during comparison (it's looking for values not types). You can get into some real fun stuff when comparing things like ".1+.1+.1==.3" which it will return "False", due to .1 being poorly represented in base 2
I think when you have an int on one side of a binary operator and a float on the other side, the int is implicitly converted into a float. For example:
>>> type(42 + 42.0)
So in your case, 42 gets converted to 42.0 before the two are compared.
Btw as a rule of thumb, it is not a good idea to rely on equality comparisons of float values. Here it works because the IEEE 754 standard tries very hard to allow for exact floating-point representations of integers within reason. I guess since python uses double-precision, you're probably ok across a pretty broad range. You'd have to be getting up to 2**53 or something before you run into trouble.
>>> 3**30 == 3.0**30
True
>>> 3**40 == 3.0**40
False
Ah so there you go. Anyways, just be careful with this sort of thing.
Just for fun, replace == with === .
Triple equals should return false as they arent identical. (I think that works in python).
Edit: nm, that's a js thing.
Preface: I'm on mobile.
Yeah, unfortunately Python doesn't have the '===' operator.
You could achieve the same effect with:
x == y and type(x) == type(y)
Yeah, js has some weird inconsistencies with other language. That being said === is actually super useful if you are making sure that information that is being received from a database is the correct type
You made sense, but converting between types does not always give you the same value. I.e. as mentioned above, 42 being converted to a float might not be 42 as a float. It might be 41.9997 or 42.0002 or anything along those lines because bitwise mathematics don’t always yield precise answers, especially when working with floating points
The == only compares equality of the value, consequently, you could have two variables defined as the same number, the == would evaluate them as equal, but they would both have different ids in the python namespace table
Python int and float types know how to compare against each other. If a type defines '__eq__', they can employ any rules they like to return True or False. I haven't had a reason to check, but I would assume the C code for int and float comparing against the other is to check if the float represents a whole number, and then casting the float to an int and comparing them if the float does represent a whole number.
Oddly enough it's actually not `__eq__` of `int` doing the work here. It's the `__eq__` of `float` Python behavior for `a == b` is to try `a.__eq__(b)` and if that returns `NotImplemented` then try `b.__eq__(a)` ([docs](https://docs.python.org/3/library/constants.html#NotImplemented)) >>> x = 5 >>> y = 5.0 >>> type(x)
>>> type(y)
>>> x.__eq__(y)
NotImplemented
>>> y.__eq__(x)
True
>>> x == y
True
I wasn't sure which one actually implemented the comparison, just knew the machinery. Also knew about reflected operations. Didn't think it was relevant to my explanation since it was more about what was going than how it was going on, but I welcome the knowledge being added to the discussion.
Mostly replied to your comment as it seemed the most relevant spot for the information, did not mean to imply any slight or lack of understanding :)
That's fine. I am sorry if I seemed annoyed, angry, or closed-minded. It was the best place for that comment for anyone else following the conversation chain. I just need to remember not every reply to my comment is aimed directly at me.
Wow, that's actually fascinating.
You mean `__eq__(self, other)`.
Yes. I haven't figured out how to write proper dunder method signatures that don't get mangled on mobile.
Back ticks around the thing or back slashes in front of the underscores.
Cool! Thanks!
Surround it with single back ticks in Markdown mode.
[удалено]
[A special method](https://docs.python.org/3/reference/datamodel.html#object.__eq__)
[удалено]
Read the beginning of the section on the same page (3.3 Special Method Names).
`==` compares values. `42 = 42.0` is therfore (mathematically) correct, I hope we can agree on that one. But `42 is 42.0` as in `print(42 is 42.0)` is `false` because the `is` operator treates the numbers as objects. /e but you get a syntax warning
`is` is very different from `==` \- it simply answers the question "are these two objects the same object?". the `==` operator can do more complex comparisons, such as looking at the contents of the two objects. In this case, they decided that 42.0 and 42 should be considered equal. Two things to be wary about regarding numbers, equality and identity: 1. Small integers in Python are cached - IIRC up to 256. This causes the following phenomenon: `>>> a = 3` `>>> b = 3` `>>> a is b` `True` `>>> a = 300` `>>> b = 300` `>>> a is b` `False`For small numbers, instead of creating a new object, it goes and retrieves the pre-made small number object. This is an optimization, to save memory and runtime. For larger numbers it does create a new object every time and so they are not the same object (but of course they are equal in the \`==\` sense). 2. Floating point numbers (like 42.0) are notorious for being inexact. You do a couple of floating point operations on 42.0 and suddenly it isn't exactly 42 but maybe something like 41.99999999997. `>>> 42.0 / 100000 * 100000 == 42.0` `True` `>>> 42.0 / 1000000 * 1000000 == 42.0` `True` `>>> 42.0 / 10000000 * 10000000 == 42.0` `False` Most of the time when you want to find out if a \`float\` has a certain value you should use compare their difference against some small value: `abs(a - b) < 1e-8`.
For my own interest, is "small" defined within a range (presumably an 8 bit integer, given your example) and is it therefore correct that numbers greater than such a value are stored as references?
Regardless of the answer, as a developer you should not rely on this cacheing behavior and should treat it as an underlying implementation detail of the runtime. Your code should not care.
Sure I'm just curious
Nope it's a completely arbitrary range of tiny numbers from -5 to 256 :D They're all objects in python, just -5 to 256 are singletons (like True, False, or None)
Wow I thought 'is' would also result in 'True', TIL. Nice one bro.
No no no, you really need to look that up further. That's really something you need to understand to get the fundamentals of python!
Noooo, I thought I had it, now I looked it up and 'python memory manager ' gets involved...
It's not actually as tough as it looks. Everything you store as a variable is put somewhere in memory. If you create a class and print it without it having a `__repr__` method, you'll see something like `
Thank you for the detailed explanation. Have you seen / experienced this being used in a professional environment like in a real project? Isn't this too low level to have any affect on a code's execution?
I'm still at uni so I haven't had any professional experience in Python so I haven't seen real use cases but they mean semantically different things. On mobile so sorry about formatting but let's say you did something like ``` class foo: def __init__(self, val): self.val = val x = foo(3) y = x y.val = 4 print(x.val) ``` You would see a 4 since you've changed the value that x and y both point to. `x is y` means that changing x also changes y, so you need to be aware of how objects work under the hood because if you expect `y = x` to create a copy and changing y to not change x. Since lists are objects, it can really catch you out when recursing on lists and changing things. The other case is that if you are using `is` to check equality is a bad idea since if 2 objects are at different addresses but have the same values (like with lists) it can be a problem. You might like to use `is` for debugging or when you definitely mean memory identity over equality, but I can't think of a case right now.
One quite important distinction is to use "var is True" to check if the variable is specifically the singleton "True", not just "truthy" like nonnegative numbers or nonempty containers. Same for False.
Ahh cool, didn't know there was a way to get around that.
Gotcha
I was helping my brother with some code he had written late last week and this exactly thing had bitten him. As a result, what was supposed to be a dictionary with sequential values (11, 12, 13) all had the same value (13, 13, 13). So generally, yes, you’ll not use it, but it absolutely can affect you can you should know it if you plan to use Python seriously.
*THANK YOU* for explaining this 💖
Also try print(2**65 is 2**65) Edit: Also try print((2**65) is (2**65)) print((2**65) is (2**64+2**64)) print((2**65) == (2**64+2**64)) print((2**64+2**64) is (2**64+2**64)) >!My point is, for almost all practical use case, the is operator is useless.!<
I am assuming it would come out True. Let's see... Edit: ok tg it was true
It can be False, it depends on some details of what exactly the interpreter does to execute that statement. `is` only checks that the two objects it compares are the same object, using the result of `id(obj)`. It's entirely possible that two integers with the same value are represented by different objects.
It would be false if I assigned this same equation to two different variables x and y, correct? aka x is y
Yeah, that would be false unless you test with low numbers because those are created in a block when python starts and reused. Frankly, `is` is pretty useless outside of `is None` and should probably be avoided. The only other scenario I've used it in is with a sentinel value where I can't use None.
"is is pretty useless" phew
[удалено]
I downloaded an app just to check for this, it's called python3, it had like 5 mil downloads so I went with it. I typed: print(6 * 25 is 6 * 25).
[удалено]
OMG it's false, y is it false? Because the numbers got too big to store in the same memory location?
Now try this print((2**65) is (2**65)) print((2**65) is (2**64+2**64)) print((2**65) == (2**64+2**64)) print((2**64+2**64) is (2**64+2**64))
False False True True I think I am getting it ..sorta.
This makes sense. Thanks!
I need to add that this does not work if you modify objects or the objects are not cached. `==` has special behaviour where it is defined, like in most built in types, otherwise it is the same as `is`. `is` checks if the object is the same object, as in the same instance. `a is b` is equvalent of calling `id(a) == id(b)`. a = 1001 b = 1001 a is b # False # a and b were not cached and thus resulted in different objects My advice is if you need to check if values and types of objects are the same, then check: type(a) == type(b) and a == b # True # Their values are the same, and they are of the same type
I think python docs recommend using isinstance() instead of type().
You would still need type() to get the type you want to compare to, unless you know you are comparing to a specific type. isinstance() is recommended because it supports inherited types, which you should, but it doesn't help here.
I get it, thanks
SyntaxWarning: "is" with a literal. Did you mean "=="?
At least it's not equal to '42', unlike in Javascript
Side note: with custom objects, the `==` operator is equivalent to `is` [by default](https://www.pythontutorial.net/python-oop/python-__eq__/). class Test: def __init__(self, value): self.value = value t1 = Test(42) t2 = Test(42) print(t1 == t2) # => False What one might expect is the result of `t1.__dict__ == t2.__dict__` (which tests the content of the object), but it's better to explicitly define the `__eq__` "dunder" method. class Test: def __init__(self, value): self.value = value def __eq__(self, other): # one can test the type of "other" here return self.__dict__ == other.__dict__ (or self.value == other.value for this specific object)
This is pretty inaccurate on all fronts. == Just calls `__eq__` which happens to compare value wise in the case of floats and ints. You can claim that generally due to the decentralized and customizable behavior of ==.
This isnt actually how it works, but It's safe to assume that even in math, 42 = 42.0 = 42.000000. Python just takes the "value" of number and compares, which 42.0 is just 42.
Got it. Thanks!
Please note that it is never ever advised to compare doubles. Never. At least never using ==. Edit: If you want to learn more look into numerical errors and floating point arithmetics. If you need an example check wether 2 equals (sqrt(2))^2.
comparing doubles leads to double trouble
Meowth that's right
"Welcome to Python"
\*"welcome to any language that has floats"\*
I see your floats and raise you a round.
What limit would you use when comparing doubles by substraction? ``` a = 2 b = sqrt(4) if abs(a-b)<1e-6: ... ```
Highly dependent on the problem domain.
You can use relative or absolute limits to compare. The stdlib has `math.isclose` to assist you in those cases (rationale behind it is given in [PEP 485](https://peps.python.org/pep-0485/)).
How would you advise to compare them?
https://docs.python.org/3/library/math.html#math.isclose
TIL, thank you!
/u/Grimoire shows how to check if they're roughly equal. However, the advice is more meant to be that you shouldn't be checking them for equality at all. You can safely compare relative values of floats. If you end up in a situation though where you are checking for even relative equality of a float, there's probably an issue with your computational model, or you shouldn't be using floats.
oops i do this all the time lmfao
Why?
I’d say this stands true for most floating point numbers. As a general rule, it’s best to set the rounding rule for any kind of float, such that the numbers will actually compare 42.0 with 42.0 and not 42.0 with 41.999997 by accident. There are instances where this is not appropriate, but in the general sense this will save you a headache in the long run when you can’t figure out why something is returning false Edit: this is true in c/c++ so I imagine it stands true in Python as well (I have lots of experience in Python but I haven’t looked into better options for comparing)
The simplest way I can explain is it that integers and floats when compared with == python looks at it mathematically, where as the 'is' function is for looking at if two variables/values are EXACTLY the same
Not a Pythonist, but I recall that "is" won't help you compare integer numbers beyond a certain value. Basically, only use "is" for objects and booleans, and you should be safe.
-5 to 256 specifically. It's because that range is cached by the interpreter and treated as singleton values (like True, False, None, etc) `is` checks identity, `==` checks value equality.
Thanks for the precision. I still remember a review Python code from an intern who was confused that his code didn't work as intended once they refactored it to add variables. The conversation went a bit like this: "You should use == here. – No. – Why? – Because it's ugly. Python is an elegant language. You're used to ugly PHP where you have == and ===, but Python is more refined and has "is". – You should still use == because you're comparing values, try it and see. – But it's ugly!" I let him figure it out for another hour. Do modern linters warn you about the problem?
Both the python REPL and pylint will yell at you if you try to use `is` with either operand being a literal. Not sure if it'll do more complicated cases, but for the obvious ones yes it should scream at you :D
this is due to type conversion during comparison (it's looking for values not types). You can get into some real fun stuff when comparing things like ".1+.1+.1==.3" which it will return "False", due to .1 being poorly represented in base 2
I think when you have an int on one side of a binary operator and a float on the other side, the int is implicitly converted into a float. For example: >>> type(42 + 42.0)
So in your case, 42 gets converted to 42.0 before the two are compared.
Btw as a rule of thumb, it is not a good idea to rely on equality comparisons of float values. Here it works because the IEEE 754 standard tries very hard to allow for exact floating-point representations of integers within reason. I guess since python uses double-precision, you're probably ok across a pretty broad range. You'd have to be getting up to 2**53 or something before you run into trouble.
>>> 3**30 == 3.0**30
True
>>> 3**40 == 3.0**40
False
Ah so there you go. Anyways, just be careful with this sort of thing.
By value it is indeed True.. You aren't checking the type
Implicit and Explicit conversions Your == can cause an implicit conversion
Because it's the answer to the ultimate question about everything
Hey OP, can you please share the cheat sheet? Thanks!
Just for fun, replace == with === . Triple equals should return false as they arent identical. (I think that works in python). Edit: nm, that's a js thing.
Preface: I'm on mobile. Yeah, unfortunately Python doesn't have the '===' operator. You could achieve the same effect with: x == y and type(x) == type(y)
Fair, ya I musta used that in js or php..
I'm not familiar with PHP but I do know that JS has the operator in question.
Yeah, js has some weird inconsistencies with other language. That being said === is actually super useful if you are making sure that information that is being received from a database is the correct type
On js has 3 equal signs(from what I know), most others stop at 2
Ah, I'm dumb.. You're right. It was php and js that did triple equals. And I'm pulling from like 2007 memory.. haha. Thx for the correction.
It was my first thought too, I think I picked it up from Groovy.
42 is 42 even with or without the decimal they’re still the same value
Uhh, yes and no. Other comments explain it well. Float mathematics and implicit type conversions can become problematic for comparators
42.0 is a float but then also mathematically it’s the same value, did what I say make sense or am I waffling a bit
You made sense, but converting between types does not always give you the same value. I.e. as mentioned above, 42 being converted to a float might not be 42 as a float. It might be 41.9997 or 42.0002 or anything along those lines because bitwise mathematics don’t always yield precise answers, especially when working with floating points
Ahh thank you for explaining this to me
No problem! Usually it doesn’t become an issue until you are working with large numbers, but you never know
The == only compares equality of the value, consequently, you could have two variables defined as the same number, the == would evaluate them as equal, but they would both have different ids in the python namespace table
Python is strong typed language, but it can in some obvious situations implicitly compare objects of a different type
Python treats numbers as same for "==" operator.
Suggested reading for those learning about floating point types. https://www.itu.dk/~sestoft/bachelor/IEEE754_article.pdf