1

I'm learning about logic gates and was messing around with them when I found something interesting. This is my code:

for i in range (4):
    p = math.floor(i/2)
    q = i % 2
    a = p and q 
    b = not(not(p or q) or not(q) or not(p))
        
    print(str(p) +"\t"+ str(q) + "\t"+ str(a)+"\t"+str(b))

And this is the result:

0       0       0       False
0       1       0       False
1       0       0       False
1       1       1       True

Why doesn't Python either always print a 1 or a 0 or always print True or False?

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
janhetjoch
  • 53
  • 1
  • 2
  • 9
  • 5
    Because `b` will create a boolean, `not` always returns a boolean, `and` returns one fo it's arguments (just like `or`). – juanpa.arrivillaga Mar 08 '22 at 17:44
  • 1
    To be clear, "why" as in "why did the Python development team choose to implement it this way?" is off-topic. Our scope ends as soon as "how do I write code to get my desired behavior?" is answered (that being the _practical and answerable_ subset). – Charles Duffy Mar 08 '22 at 18:09
  • ...on that point, see [Are language specs and their developments on-topic?](https://meta.stackoverflow.com/questions/306471/are-language-specs-and-their-developments-on-topic) and [What is the rationale for closing "why" questions on a language design?](https://meta.stackexchange.com/questions/170394/what-is-the-rationale-for-closing-why-questions-on-a-language-design). Our goal is to help people practice software development; speculating on accidents of history does not assist in that. – Charles Duffy Mar 08 '22 at 18:13
  • @CharlesDuffy I'm sorry if this wasn't the place to ask, I was just curious about a programming thing and didn't think much more than programing question ==> stack overflow. I see how this could be annoying. Do you maybe know of a place to ask similar questions in the future? – janhetjoch Mar 08 '22 at 18:39
  • To be honest, I'm going to withdraw my complaint. One of the general rules here is that good answers are more important than good questions, and you ended up getting some really great ones. :) -- now, to answer your question, if this were purely a design-philosophy question it might be better suited to [programmers.se], but since it can be argued to have a "how do I avoid this behavior in practice?" or please-explain-this-code aspect in addition to the theory/design-philosophy piece, they'd likely reject it as asked right now. – Charles Duffy Mar 08 '22 at 19:40

2 Answers2

3

math.floor returns a number.

Operators between two numbers (such as modulo) also return numbers.

not returns a bool, not an int

Because not has to create a new value, it returns a boolean value regardless of the type of its argument - spec

If you want it to be an int (and simplify the print statement)

print('\t'.join(map(str, [p, q, a, int(b)])
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • As soon as I find the method signature for the logical operators, I will tell :) – OneCricketeer Mar 08 '22 at 17:53
  • 1
    For convenience... ***"The expression `x and y` first evaluates `x`; if `x` is false, its value is returned; otherwise, `y` is evaluated and the resulting value is returned."***, ***"The expression `x or y` first evaluates `x`; if `x` is true, its value is returned; otherwise, `y` is evaluated and the resulting value is returned."*** – CryptoFool Mar 08 '22 at 17:55
  • 1
    Do you happen to know why the developers of python decided that and and or shouldn't return booleans, can they ever return something other than1 or 0? I wouldn't expect so, so why make it return an int? Don't ints take up more space than bools? – janhetjoch Mar 08 '22 at 17:57
  • 1
    @janhetjoch, eh? `True` and `False` are interned singletons; there's only one copy of each of those two objects in memory, so a reference to that existing space only takes as much memory as any other reference. – Charles Duffy Mar 08 '22 at 17:58
  • @janhetjoch, ...that also means that `is` (and its pointer-arithmetic equivalents in C) can be used to evaluate them, making such evaluations very, very fast. – Charles Duffy Mar 08 '22 at 17:59
  • 1
    My guess is that it was done this way for flexibility. There's a much wider variety of use cases for the operators the way they are defined vs if they threw away the values of the operands and always returned True/False. Any time you're testing for "truthiness", like in `if`, `while` statements, etc. , you don't care. – CryptoFool Mar 08 '22 at 17:59
  • @janhetjoch `"a" and "b"` returns `"b"`... See [Logical operators in Python](https://stackoverflow.com/q/44612144/6045800) – Tomerikoo Mar 08 '22 at 18:06
3

The not unary operator always returns a bool value. It needs to, as it needs to return the boolean inverse of whatever value it was passed, which is always a new value (not its argument).

The real odd part of your chart is actually the third column, which shows that the and operator does return numbers if it's passed them on both sides. You might expect and to be a boolean operator just like not, but it's slightly different. The reason is that and always returns one of its arguments. If the first argument is falsey, that value is what is returned. If the first argument is truthy, the second argument is returned.

You can see this if you test with values other than just 0 and 1:

print(3 and 2)       # prints 2
print([] and [1,2])  # prints []

The or operator also does this (in a slightly different manner), but that's covered up in your calculation of b by the not calls.

This behavior of and and or is called short-circuiting. It's big advantage is that it lets Python avoid interpreting expressions it doesn't need to get the value of the boolean expression. For instance, this expression will not call the slow_function:

result = falsey_value and slow_function()

It also lets you guard expressions that would cause exceptions if they were evaluated:

if node is not None and node.value > x:
     ...

If the node variable is None in that code, evaluating node.value would give an AttributeError (since None doesn't have a value attribute). But because and short circuits, the first expression prevents the second expression from being evaluated when it's invalid.

Blckknght
  • 100,903
  • 11
  • 120
  • 169