Загадка для неспящих любителей Python. Почему где-то в результате число, а где то boolean?

Пост начинается с твита. В реплаях не было подробного пояснения, поэтому я решил сделать свое маленькое исследование

Круг 0. Запустим

for x in 0,1:
  for y in 0,1:
    for z in 0,1:
      print (x,y,z, 'result: ', (x and y) or (x and not z))
0 0 0 result:  0
0 0 1 result:  0
0 1 0 result:  0
0 1 1 result:  0
1 0 0 result:  True
1 0 1 result:  False
1 1 0 result:  1
1 1 1 result:  1

Круг 1

Наблюдения 1

В некоторых случаях выражение возвращает int, а в некоторых boolean

Предположим 1

Дело не в операторе OR и разделим на две проверки

for x in 0,1:
  for y in 0,1:
    for z in 0,1:
      print (x,y,z, 'result1: ', (x and y))
      print (x,y,z, 'result2: ', (x and not z))
0 0 0 result1:  0
0 0 0 result2:  0
0 0 1 result1:  0
0 0 1 result2:  0
0 1 0 result1:  0
0 1 0 result2:  0
0 1 1 result1:  0
0 1 1 result2:  0
1 0 0 result1:  0
1 0 0 result2:  True
1 0 1 result1:  0
1 0 1 result2:  False
1 1 0 result1:  1
1 1 0 result2:  True
1 1 1 result1:  1
1 1 1 result2:  False

Круг 2

Наблюдения 2

Boolean переменные вылезают только в result2, так что на result1 сейчас можно не смотреть Так как единственной разницей между вычисляемыми result в наличии not

Предположим 2

Дело только в not и выведем x, y и z с и без not. Также посмотрим на not not выражение

for x in 0,1:
  for y in 0,1:
    for z in 0,1:
      print ('x:\t\t', (x))
      print ('not x:\t\t', (not x))
      print ('y:\t\t', (y))
      print ('not y:\t\t', (not y))
      print ('z:\t\t', (z))
      print ('not z:\t\t', (not z))
      print ('not not z:\t', (not not z))
x: 0
not x: True
y: 0
not y: True
z: 0
not z: True
not not z: False
x: 0
not x: True
y: 0
not y: True
z: 1
not z: False
not not z: True
x: 0
not x: True
y: 1
not y: False
z: 0
not z: True
not not z: False
x: 0
not x: True
y: 1
not y: False
z: 1
not z: False
not not z: True
x: 1
not x: False
y: 0
not y: True
z: 0
not z: True
not not z: False
x: 1
not x: False
y: 0
not y: True
z: 1
not z: False
not not z: True
x: 1
not x: False
y: 1
not y: False
z: 0
not z: True
not not z: False
x: 1
not x: False
y: 1
not y: False
z: 1
not z: False
not not z: True

Круг 3

Наблюдения 3

  • Все строки с not стали boolean

Предположение 3

Необходимо проверить, как работают and и or с not

for x in 0,1:
    for y in 0,1:
        print (x, 'AND', y, ':\t\t', x and y)
        print (x, 'AND NOT', y, ':\t\t', x and not y)
        print ('NOT', x, 'AND', y, ':\t\t', not x and y)
        print ('NOT', x, 'AND', 'NOT', y, ':\t', not x and not y)

        print (x, 'OR', y, ':\t\t', x or y)
        print (x, 'OR', 'NOT', y, ':\t\t', x or not y)
        print ('NOT', x, 'OR', y, ':\t\t', not x or y)
        print ('NOT', x, 'OR', 'NOT', y, ':\t', not x or not y)
0 AND 0 : 0
0 AND NOT 0 : 0
NOT 0 AND 0 : 0
NOT 0 AND NOT 0 : True
0 OR 0 : 0
0 OR NOT 0 : True
NOT 0 OR 0 : True
NOT 0 OR NOT 0 : True
0 AND 1 : 0
0 AND NOT 1 : 0
NOT 0 AND 1 : 1
NOT 0 AND NOT 1 : False
0 OR 1 : 1
0 OR NOT 1 : False
NOT 0 OR 1 : True
NOT 0 OR NOT 1 : True
1 AND 0 : 0
1 AND NOT 0 : True
NOT 1 AND 0 : False
NOT 1 AND NOT 0 : False
1 OR 0 : 1
1 OR NOT 0 : 1
NOT 1 OR 0 : 0
NOT 1 OR NOT 0 : True
1 AND 1 : 1
1 AND NOT 1 : False
NOT 1 AND 1 : False
NOT 1 AND NOT 1 : False
1 OR 1 : 1
1 OR NOT 1 : 1
NOT 1 OR 1 : 1
NOT 1 OR NOT 1 : False

Круг 4

Наблюдения 4

  • Если первый аргумент or является true или 1, то в целях оптимизации (?) вторая половина не проверяется. Тип первого аргумента сохраняется.
  • Если первый аргумент or является False или 0, то его тип данных ингорируется, наследуется только тип второго аргумента.
  • Тип первого аргумента and игнорируется, наследуется только тип второго агрумента
  • not всегда приводит вывод к boolean типу

Выводы

Будьте, блядь, предельно осторожны, duck typing - крайне непредсказуемая штука