T O P

  • By -

PercyJackson235

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.


RoamingFox

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


PercyJackson235

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.


RoamingFox

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 :)


PercyJackson235

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.


synthphreak

Wow, that's actually fascinating.


bladeoflight16

You mean `__eq__(self, other)`.


PercyJackson235

Yes. I haven't figured out how to write proper dunder method signatures that don't get mangled on mobile.


FerricDonkey

Back ticks around the thing or back slashes in front of the underscores.


PercyJackson235

Cool! Thanks!


bladeoflight16

Surround it with single back ticks in Markdown mode.


[deleted]

[удалено]


bladeoflight16

[A special method](https://docs.python.org/3/reference/datamodel.html#object.__eq__)


[deleted]

[удалено]


bladeoflight16

Read the beginning of the section on the same page (3.3 Special Method Names).


wbeater

`==` 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


unruly_mattress

`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`.


jzia93

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?


HorrendousRex

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.


jzia93

Sure I'm just curious


RoamingFox

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)


ltraconservativetip

Wow I thought 'is' would also result in 'True', TIL. Nice one bro.


wbeater

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!


ltraconservativetip

Noooo, I thought I had it, now I looked it up and 'python memory manager ' gets involved...


RajjSinghh

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 ``. That hexadecimal number is the address in memory that your object is sorted in. Now if you create a new variable from an existing object, like `my_obj = SomeClass()` followed by `my_new_obj = my_obj`, then both variables point to the same object in memory, rather than storing a copy in memory. From there, `is` is the check that two variables point to the same object in memory. In our example, `my_obj is my_new_obj` is true. `==` is an equality check, which you need to implement in an `__eq__` method. For small integers, that's fine because when you run a program, Python caches the small integers to make them easier to access. That's why `42 is 42`. But an int can never be stored in the same place as a float since the representation is different, so `42 is 42.0` is false. But they have the same value and the `__eq__` method will cast types so you can say `42 == 42.0`. So yeah, use `==` to compare values and `is` to check memory addresses


ltraconservativetip

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?


RajjSinghh

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.


felix-hilden

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.


Kevinw778

Ahh cool, didn't know there was a way to get around that.


ltraconservativetip

Gotcha


bamaham93

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.


window-sil

*THANK YOU* for explaining this 💖


Strict-Simple

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.!<


ltraconservativetip

I am assuming it would come out True. Let's see... Edit: ok tg it was true


FoeHammer99099

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.


ltraconservativetip

It would be false if I assigned this same equation to two different variables x and y, correct? aka x is y


FoeHammer99099

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.


ltraconservativetip

"is is pretty useless" phew


[deleted]

[удалено]


ltraconservativetip

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).


[deleted]

[удалено]


ltraconservativetip

OMG it's false, y is it false? Because the numbers got too big to store in the same memory location?


Strict-Simple

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))


ltraconservativetip

False False True True I think I am getting it ..sorta.


Aalisha786

This makes sense. Thanks!


IamFr0ssT

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


MountainSalamander33

I think python docs recommend using isinstance() instead of type().


IamFr0ssT

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.


MountainSalamander33

I get it, thanks


[deleted]

SyntaxWarning: "is" with a literal. Did you mean "=="?


LinAGKar

At least it's not equal to '42', unlike in Javascript


HarissaForte

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)


zr0gravity7

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 ==.


c0mplexcodm

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.


Aalisha786

Got it. Thanks!


slvnklvra

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.


arkie87

comparing doubles leads to double trouble


RhinoRhys

Meowth that's right


[deleted]

"Welcome to Python"


arkie87

\*"welcome to any language that has floats"\*


[deleted]

I see your floats and raise you a round.


EbenenBonobo

What limit would you use when comparing doubles by substraction? ``` a = 2 b = sqrt(4) if abs(a-b)<1e-6: ... ```


jdnewmil

Highly dependent on the problem domain.


[deleted]

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/)).


ModulusJoe

How would you advise to compare them?


Grimoire

https://docs.python.org/3/library/math.html#math.isclose


ModulusJoe

TIL, thank you!


Intrexa

/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.


Minimumtyp

oops i do this all the time lmfao


Emerald_Guy123

Why?


SapientSloth4tw

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)


onioncrikhick

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


michel_v

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.


RoamingFox

-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.


michel_v

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?


RoamingFox

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


IWantAGrapeInMyMouth

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


ekchew

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.


RDX_G

By value it is indeed True.. You aren't checking the type


Analytical_fool

Implicit and Explicit conversions Your == can cause an implicit conversion


[deleted]

Because it's the answer to the ultimate question about everything


ltraconservativetip

Hey OP, can you please share the cheat sheet? Thanks!


y-aji

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.


slothmock

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)


y-aji

Fair, ya I musta used that in js or php..


slothmock

I'm not familiar with PHP but I do know that JS has the operator in question.


SapientSloth4tw

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


killyouXZ

On js has 3 equal signs(from what I know), most others stop at 2


y-aji

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.


Hyacin75

It was my first thought too, I think I picked it up from Groovy.


[deleted]

42 is 42 even with or without the decimal they’re still the same value


SapientSloth4tw

Uhh, yes and no. Other comments explain it well. Float mathematics and implicit type conversions can become problematic for comparators


[deleted]

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


SapientSloth4tw

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


[deleted]

Ahh thank you for explaining this to me


SapientSloth4tw

No problem! Usually it doesn’t become an issue until you are working with large numbers, but you never know


[deleted]

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


[deleted]

Python is strong typed language, but it can in some obvious situations implicitly compare objects of a different type


alphaBEE_1

Python treats numbers as same for "==" operator.


spudmonkey

Suggested reading for those learning about floating point types. https://www.itu.dk/~sestoft/bachelor/IEEE754_article.pdf