'2'<'1'== False #False
('2'<'1')== False #True
'2'<('1'== False) #error
code in python3 we know operator precedence in python3 https://docs.python.org/3/reference/expressions.html#operator-precedence
'2'<'1'== False #False
('2'<'1')== False #True
'2'<('1'== False) #error
code in python3 we know operator precedence in python3 https://docs.python.org/3/reference/expressions.html#operator-precedence
'2'<'1'== False
it is evaluated as '2'<'1' and '1' == False
according to the operator chaining {thanks @ymonad to provide this link}
which will evaluated to be False
('2'<'1')== False
as ()
have higher precedence so will be evaluated first. so the expression will be reduced to False == False
which will evaluate to be True
'2'<('1'== False)
first ('1' == False)
is evaluated which is False
but now the operation is '2'<False
which is an illegal operation in python
EDIT:
To answer a question raised by @snr in the comment section
The vital question is why '1'== False is valid while '2'< bool is not
It is because the default behaviour for equality comparison (== and !=) is based on the identity of the objects, and as object False
and object '1'
does not share the same identity thus the result will be False
whereas a default behaviour of other comparison (<, >, <=, and >=) is not provided thus an attempt to do so raises TypeError
you can find this in documentation link provided by OP (under the heading value-comparisions)
When python tries to do string < string
then it turns into ascii and it orders it by alphabet order.
https://careerkarma.com/blog/python-compare-strings/
By the way, 1 ascii is 49 and 2 ascii is 50
'2'<'1'== False #False
it is evaluated as '2'<'1' and '1' == False
which is False
because
50 < 49 and 1 == False
-> False and False
-> False
('2'<'1')== False # True
it makes python do () first.
(50 < 49) == False
->
False == False
-> True
'2'<('1'== False) #error
first ('1' == False)
is evaluated which is False and you try to do '2'<False
.
And it get's an error.
Python Operator Precedence: https://www.mathcs.emory.edu/~valerie/courses/fall10/155/resources/op_precedence.html
I really tried to dig into but I can't get an answer which satisfies me. However, a person who has some time can walk through the following.
PyObject *tp_richcompare(PyObject *self, PyObject *other, int op);
methods runs under the hood. Its implementation is here.
As far as I understand, it firstly checks whether both PyObject
s are comperable by inspecting the object types
by e.g.
(!Py_IS_TYPE(v, Py_TYPE(w)) &&
PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
(f = Py_TYPE(w)->tp_richcompare) != NULL)
As we can see also in your code, while <class 'str'> __eq__ <class 'int'>
is meant to 'Yes, they are compatible with each other w.r.t. the comparison.', <class 'str'> __lt__ <class 'bool'>
is the opposite. We are pretty sure that one of the last stations is here.
print(type(1))
print(type('1'))
print(type(False))
print(type('1' == 1))
print('1' == 1) #False <class 'str'> __eq__ <class 'int'>
print(('2' < '1')== False) #True
#print('2'<('1'== False)) <class 'str'> __lt__ (<class 'str'> __eq__ <class 'bool'>)
# (<class 'str'> __eq__ <class 'bool'>) returns <class 'bool'>, then as a result
# <class 'str'> __lt__ <class 'bool'> comparison is not implemented.
As a side note, its satisfactory answer, at least for me, must include detailed C explanations, not Pythonic