truth table: Binary Operations 2


requirements

Binary Operations 1


preview

Here are the tests I have at the end of this chapter

  1import src.truth_table
  2import unittest
  3
  4
  5class TestBinaryOperations(unittest.TestCase):
  6
  7    def test_contradiction(self):
  8        self.assertFalse(
  9            src.truth_table.contradiction(True, True)
 10        )
 11        self.assertFalse(
 12            src.truth_table.contradiction(True, False)
 13        )
 14        self.assertFalse(
 15            src.truth_table.contradiction(False, True)
 16        )
 17        self.assertFalse(
 18            src.truth_table.contradiction(False, False)
 19        )
 20
 21    def test_logical_conjunction(self):
 22        self.assertTrue(
 23            src.truth_table.logical_conjunction(True, True)
 24        )
 25        self.assertFalse(
 26            src.truth_table.logical_conjunction(True, False)
 27        )
 28        self.assertFalse(
 29            src.truth_table.logical_conjunction(False, True)
 30        )
 31        self.assertFalse(
 32            src.truth_table.logical_conjunction(False, False)
 33        )
 34
 35    def test_project_second(self):
 36        self.assertTrue(
 37            src.truth_table.project_second(True, True)
 38        )
 39        self.assertFalse(
 40            src.truth_table.project_second(True, False)
 41        )
 42        self.assertTrue(
 43            src.truth_table.project_second(False, True)
 44        )
 45        self.assertFalse(
 46            src.truth_table.project_second(False, False)
 47        )
 48
 49    def test_converse_non_implication(self):
 50        self.assertFalse(
 51            src.truth_table.converse_non_implication(True, True)
 52        )
 53        self.assertFalse(
 54            src.truth_table.converse_non_implication(True, False)
 55        )
 56        self.assertTrue(
 57            src.truth_table.converse_non_implication(False, True)
 58        )
 59        self.assertFalse(
 60            src.truth_table.converse_non_implication(False, False)
 61        )
 62
 63    def test_negate_first(self):
 64        self.assertFalse(src.truth_table.negate_first(True, True))
 65        self.assertFalse(src.truth_table.negate_first(True, False))
 66        self.assertTrue(src.truth_table.negate_first(False, True))
 67        self.assertTrue(src.truth_table.negate_first(False, False))
 68
 69    def test_logical_nand(self):
 70        self.assertFalse(src.truth_table.logical_nand(True, True))
 71        self.assertTrue(src.truth_table.logical_nand(True, False))
 72        self.assertTrue(src.truth_table.logical_nand(False, True))
 73        self.assertTrue(src.truth_table.logical_nand(False, False))
 74
 75    def test_tautology(self):
 76        self.assertTrue(src.truth_table.tautology(True, True))
 77        self.assertTrue(src.truth_table.tautology(True, False))
 78        self.assertTrue(src.truth_table.tautology(False, True))
 79        self.assertTrue(src.truth_table.tautology(False, False))
 80
 81    def test_logical_disjunction(self):
 82        self.assertTrue(
 83            src.truth_table.logical_disjunction(True, True)
 84        )
 85        self.assertTrue(
 86            src.truth_table.logical_disjunction(True, False)
 87        )
 88        self.assertTrue(
 89            src.truth_table.logical_disjunction(False, True)
 90        )
 91        self.assertFalse(
 92            src.truth_table.logical_disjunction(False, False)
 93        )
 94
 95
 96# Exceptions seen
 97# AttributeError
 98# TypeError
 99# AssertionError
100# SyntaxError

continue the project

  • Make sure you are in the pumping_python folder with pwd in the terminal

    pwd
    

    if the terminal shows anything other than

    .../pumping_python
    

    change directory to the pumping_python folder

  • Once in pumping_python, change directory to the project

    cd truth_table
    

    the terminal shows

    .../pumping_python/truth_table
    
  • I run the tests with pytest-watcher

    uv run pytest-watcher . --now
    

    the terminal shows

    rootdir: .../pumping_python/truth_table
    configfile: pyproject.toml
    collected 8 items
    
    tests/test_binary.py ....                                     [ 50%]
    tests/test_nullary_unary.py ....                              [100%]
    
    ======================== 8 passed in G.HIs =========================
    
  • I hold ctrl (Windows) or option (MacOS) on the keyboard, then click on tests/test_binary.py with the mouse to open it in the editor


test_negate_first


RED: make it fail


I add a new test for negate_first in test_binary.py

49    def test_converse_non_implication(self):
50        self.assertFalse(
51            src.truth_table.converse_non_implication(True, True)
52        )
53        self.assertFalse(
54            src.truth_table.converse_non_implication(True, False)
55        )
56        self.assertTrue(
57            src.truth_table.converse_non_implication(False, True)
58        )
59        self.assertFalse(
60            src.truth_table.converse_non_implication(False, False)
61        )
62
63    def test_negate_first(self):
64        self.assertFalse(src.truth_table.negate_first(True, True))
65
66
67# Exceptions seen

the terminal shows AttributeError

AttributeError: module 'src.truth_table' has no attribute 'negate_first'

there is no definition for negate_first in truth_table.py


GREEN: make it pass


  • I open truth_table.py from the src folder in the editor

  • I add the function in truth_table.py

    29def converse_non_implication(first_input, second_input):
    30    return logical_conjunction(not first_input, second_input)
    31    return not first_input and second_input
    32
    33
    34def negate_first(first_input, second_input):
    35    return False
    

    the test passes.

negate_first returns False, if the first input is True and the second input is True


REFACTOR: make it better


  • I add the second case - when the first input is True and the second input is False, to test_binary.py

    63    def test_negate_first(self):
    64        self.assertFalse(src.truth_table.negate_first(True, True))
    65        self.assertFalse(src.truth_table.negate_first(True, False))
    

    the test is still green. negate_first returns

  • I add the next case - when the first input is False and the second input is True

    63    def test_negate_first(self):
    64        self.assertFalse(src.truth_table.negate_first(True, True))
    65        self.assertFalse(src.truth_table.negate_first(True, False))
    66        self.assertTrue(src.truth_table.negate_first(False, True))
    

    the terminal shows AssertionError

    AssertionError: False is not true
    
  • I add if statements for this case to negate_first in truth_table.py

    34def negate_first(first_input, second_input):
    35    if first_input == False:
    36        if second_input == True:
    37            return True
    38    return False
    

    the test passes. negate_first returns

  • I add the last case - when the first input is False and the second input is False to test_binary.py

    63    def test_negate_first(self):
    64        self.assertFalse(src.truth_table.negate_first(True, True))
    65        self.assertFalse(src.truth_table.negate_first(True, False))
    66        self.assertTrue(src.truth_table.negate_first(False, True))
    67        self.assertTrue(src.truth_table.negate_first(False, False))
    68
    69
    70# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: False is not true
    
  • I add an if statement for it in truth_table.py

    34def negate_first(first_input, second_input):
    35    if first_input == False:
    36        if second_input == False:
    37            return True
    38        if second_input == True:
    39            return True
    40    return False
    

    the test passes. negate_first returns

    oh! It returns the logical negation of the first input

  • I add a return statement

    34def negate_first(first_input, second_input):
    35    return not first_input
    36    if first_input == False:
    37        if second_input == False:
    38            return True
    39        if second_input == True:
    40            return True
    41    return False
    

    the test is still green

  • I remove the other statements

    34def negate_first(first_input, second_input):
    35    return not first_input
    

    still green, no need for if statements here.

Negate First always returns


test_logical_nand


RED: make it fail


I add a new test to test_binary.py

63    def test_negate_first(self):
64        self.assertFalse(src.truth_table.negate_first(True, True))
65        self.assertFalse(src.truth_table.negate_first(True, False))
66        self.assertTrue(src.truth_table.negate_first(False, True))
67        self.assertTrue(src.truth_table.negate_first(False, False))
68
69    def test_logical_nand(self):
70        self.assertFalse(src.truth_table.logical_nand(True, True))
71
72
73# Exceptions seen

the terminal shows AttributeError

AttributeError: module 'src.truth_table' has no attribute 'logical_nand'. Did you mean: 'logical_false'?

there is no definition for logical_nand in truth_table.py


GREEN: make it pass


I add the function to truth_table.py

34def negate_first(first_input, second_input):
35    return not first_input
36
37
38def logical_nand(first_input, second_input):
39    return False

the test passes. logical_nand returns False, if the first input is True and the second input is True


REFACTOR: make it better


  • I add the second case - when the first input is True and the second input is False, in test_binary.py

    69    def test_logical_nand(self):
    70        self.assertFalse(src.truth_table.logical_nand(True, True))
    71        self.assertTrue(src.truth_table.logical_nand(True, False))
    

    the terminal shows AssertionError

    AssertionError: False is not true
    
  • I add if statements to logical_nand in truth_table.py

    38def logical_nand(first_input, second_input):
    39    if first_input == True:
    40        if second_input == False:
    41            return True
    42    return False
    

    the test passes. logical_nand returns

  • I add another case, where the first input is False and the second input is True, to test_logical_nand in test_binary.py

    69    def test_logical_nand(self):
    70        self.assertFalse(src.truth_table.logical_nand(True, True))
    71        self.assertTrue(src.truth_table.logical_nand(True, False))
    72        self.assertTrue(src.truth_table.logical_nand(False, True))
    

    the terminal shows AssertionError

    AssertionError: False is not true
    
  • I add if statements to logical_nand in truth_table.py

    38def logical_nand(first_input, second_input):
    39    if first_input == False:
    40        if second_input == True:
    41            return True
    42    if first_input == True:
    43        if second_input == False:
    44            return True
    45    return False
    

    the test passes. logical_nand returns

  • I add the last case - when the first input is False and the second input is False, to test_logical_nand in test_binary.py

    69    def test_logical_nand(self):
    70        self.assertFalse(src.truth_table.logical_nand(True, True))
    71        self.assertTrue(src.truth_table.logical_nand(True, False))
    72        self.assertTrue(src.truth_table.logical_nand(False, True))
    73        self.assertTrue(src.truth_table.logical_nand(False, False))
    74
    75
    76# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: False is not true
    
  • I add an if statement to logical_nand in truth_table.py

    38def logical_nand(first_input, second_input):
    39    if first_input == False:
    40        if second_input == False:
    41            return True
    42        if second_input == True:
    43            return True
    44    if first_input == True:
    45        if second_input == False:
    46            return True
    47    return False
    

    the test passes. logical_nand returns

    • True, if the first input is False and the second input is False

    • True, if the first input is False and the second input is True

    • True, if the first input is True and the second input is False

    • False, if the first input is True and the second input is True - this is the only case where it returns False

  • I add an if statement for the one case where it returns False with an else clause for the other 3 cases

    38def logical_nand(first_input, second_input):
    39    if first_input == True:
    40        if second_input == True:
    41            return False
    42    else:
    43        return True
    44    if first_input == False:
    45        if second_input == False:
    46            return True
    47        if second_input == True:
    48            return True
    49    if first_input == True:
    50        if second_input == False:
    51            return True
    52    return False
    

    the test is still green

  • I remove the other statements and put the two if statements together

    38def logical_nand(first_input, second_input):
    39    # if first_input == True:
    40    #     if second_input == True:
    41    if first_input == True and second_input == True:
    42            return False
    43    else:
    44        return True
    

    still green

  • I remove the comments and use bool

    38def logical_nand(first_input, second_input):
    39    # if first_input == True and second_input == True:
    40    if bool(first_input) == True and bool(second_input) == True:
    41            return False
    42    else:
    43        return True
    

    green

  • I remove the commented line and == True

    38def logical_nand(first_input, second_input):
    39    # if bool(first_input) == True and bool(second_input) == True:
    40    if bool(first_input) and bool(second_input):
    41            return False
    42    else:
    43        return True
    

    still green

  • I remove bool

    38def logical_nand(first_input, second_input):
    39    # if bool(first_input) and bool(second_input):
    40    if first_input and second_input:
    41            return False
    42    else:
    43        return
    

    the test is still green

  • I want to use one return statement (a conditional expression) for everything, which means I need an if statement that returns True. I use logical negation (not) to change the else clause to the opposite of the if statement

    38def logical_nand(first_input, second_input):
    39    if first_input and second_input:
    40            return False
    41    # else:
    42    if not (first_input and second_input):
    43        return True
    

    still green

  • I move the new if statement to the top

    38def logical_nand(first_input, second_input):
    39    if not (first_input and second_input):
    40        return True
    41    if first_input and second_input:
    42            return False
    

    green

  • I change the second if statement to an else clause

    38def logical_nand(first_input, second_input):
    39    if not (first_input and second_input):
    40        return True
    41    # if first_input and second_input:
    42    else:
    43            return False
    

    green, green, green again 🎶

  • I add a conditional expression

    38def logical_nand(first_input, second_input):
    39    return True if not (first_input and second_input) else False
    40    if not (first_input and second_input):
    41        return True
    42    else:
    43            return False
    

    still green

  • I remove the if statement and use the simpler form of the ternary operator

    38def logical_nand(first_input, second_input):
    39    return not (first_input and second_input)
    40    return True if not (first_input and second_input) else False
    

    the test is still green

  • I remove the second return statement

    38def logical_nand(first_input, second_input):
    39    return not (first_input and second_input)
    

    this is the opposite (Logical Negation) of logical_conjunction

  • I add a return statement to make sure

    38def logical_nand(first_input, second_input):
    39    return not logical_conjunction(first_input, second_input)
    40    return not (first_input and second_input)
    

    green all the way

Logical NAND


Note

When there is only one if statement that returns False with an else clause

if something:
    return False
else:
    return True

I can change it with its logical negation (not)

if not something:
    return True
else:
    return False

I can then write it as a ternary operator (conditional expression)

return True if not (something) else False

which can be made simpler as

return not (something)

this means

  • if something: return False is the same as return not (something), just like

  • if something: return True is the same as return something


test_tautology


RED: make it fail


I add a test for the next Binary Operation in test_binary.py

69    def test_logical_nand(self):
70        self.assertFalse(src.truth_table.logical_nand(True, True))
71        self.assertTrue(src.truth_table.logical_nand(True, False))
72        self.assertTrue(src.truth_table.logical_nand(False, True))
73        self.assertTrue(src.truth_table.logical_nand(False, False))
74
75    def test_tautology(self):
76        self.assertTrue(src.truth_table.tautology(True, True))
77
78
79# Exceptions seen

the terminal shows AttributeError

AttributeError: module 'src.truth_table' has no attribute 'tautology'

truth_table.py does not have tautology in it


GREEN: make it pass


I add the function to truth_table.py

38def logical_nand(first_input, second_input):
39    return not logical_conjunction(first_input, second_input)
40    return not (first_input and second_input)
41
42
43def tautology(first_input, second_input):
44    return True

the test passes. tautology returns True, if the first input is True and the second input is True


REFACTOR: make it better


  • I add the next case, where the first input is True and the second input is False, to test_tautology in test_binary.py

    75    def test_tautology(self):
    76        self.assertTrue(src.truth_table.tautology(True, True))
    77        self.assertTrue(src.truth_table.tautology(True, False))
    

    the test is still green. tautology returns

  • I add the next case - when the first input is False and the second input is True

    75    def test_tautology(self):
    76        self.assertTrue(src.truth_table.tautology(True, True))
    77        self.assertTrue(src.truth_table.tautology(True, False))
    78        self.assertTrue(src.truth_table.tautology(False, True))
    

    the test is still green. tautology returns True

    • if the first input is False and the second input is True

    • if the first input is True

  • I add the last case - when the first input is False and the second input is False

    75    def test_tautology(self):
    76        self.assertTrue(src.truth_table.tautology(True, True))
    77        self.assertTrue(src.truth_table.tautology(True, False))
    78        self.assertTrue(src.truth_table.tautology(False, True))
    79        self.assertTrue(src.truth_table.tautology(False, False))
    80
    81
    82# Exceptions seen
    

    still green, there is only one result for this operation.

Tautology always returns True, it does not care about the inputs. It is the opposite of contradiction which always returns False


test_logical_disjunction


RED: make it fail


I add another test to test_binary.py

75    def test_tautology(self):
76        self.assertTrue(src.truth_table.tautology(True, True))
77        self.assertTrue(src.truth_table.tautology(True, False))
78        self.assertTrue(src.truth_table.tautology(False, True))
79        self.assertTrue(src.truth_table.tautology(False, False))
80
81    def test_logical_disjunction(self):
82        self.assertTrue(
83            src.truth_table.logical_disjunction(True, True)
84        )
85
86
87# Exceptions seen

the terminal shows AttributeError

AttributeError: module 'src.truth_table' has no attribute 'logical_disjunction'. Did you mean: 'logical_conjunction'?

there is no logical_disjunction in truth_table.py in the src folder, yet


GREEN: make it pass


I add the function to truth_table.py

41def tautology(first_input, second_input):
42    return True
43
44
45def logical_disjunction(first_input, second_input):
46    return True

the test passes. logical_disjunction returns True, if the first input is True and the second input is True


REFACTOR: make it better


  • I add the next case - when the first input is True and the second input is False, to test_logical_disjunction in test_binary.py

    81    def test_logical_disjunction(self):
    82        self.assertTrue(
    83            src.truth_table.logical_disjunction(True, True)
    84        )
    85        self.assertTrue(
    86            src.truth_table.logical_disjunction(True, False)
    87        )
    

    the test is still green. logical_disjunction returns

    so far this is the same as Tautology

  • I add the next case - when the first input is False and the second input is True

    81    def test_logical_disjunction(self):
    82        self.assertTrue(
    83            src.truth_table.logical_disjunction(True, True)
    84        )
    85        self.assertTrue(
    86            src.truth_table.logical_disjunction(True, False)
    87        )
    88        self.assertTrue(
    89            src.truth_table.logical_disjunction(False, True)
    90        )
    

    the test is still green. logical_disjunction still looks like Tautology, it returns

  • I add the fourth case, where the first input is False and the second input is False

    81    def test_logical_disjunction(self):
    82        self.assertTrue(
    83            src.truth_table.logical_disjunction(True, True)
    84        )
    85        self.assertTrue(
    86            src.truth_table.logical_disjunction(True, False)
    87        )
    88        self.assertTrue(
    89            src.truth_table.logical_disjunction(False, True)
    90        )
    91        self.assertFalse(
    92            src.truth_table.logical_disjunction(False, False)
    93        )
    94
    95
    96# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: True is not false
    
  • I add if statements for the new case to logical_disjunction in truth_table.py

    47def logical_disjunction(first_input, second_input):
    48    if first_input == False:
    49        if second_input == False:
    50            return False
    51    return True
    

    the test passes. logical_disjunction returns

    • False, if the first input is False and the second input is False - this is the only case where it returns False

    • True, if the first input is False and the second input is True

    • True, if the first input is True

  • I change the two if statements to one if statement with logical conjunction (and)

    47def logical_disjunction(first_input, second_input):
    48    # if first_input == False:
    49    #     if second_input == False:
    50    if first_input == False and second_input == False:
    51            return False
    52    return True
    

    the test is still green

  • I use not to write the statements with True

    47def logical_disjunction(first_input, second_input):
    48    # if first_input == False and second_input == False:
    49    if (not first_input == True) and (not second_input == True):
    50            return False
    51    return True
    

    still green

  • I use bool

    47def logical_disjunction(first_input, second_input):
    48    # if (not first_input == True) and (not second_input == True):
    49    if (
    50        (not bool(first_input) == True)
    51        and
    52        (not bool(second_input) == True)
    53    ):
    54            return False
    55    return True
    

    green

  • I remove == True

    47def logical_disjunction(first_input, second_input):
    48    # if (
    49    #     (not bool(first_input) == True)
    50    #     and
    51    #     (not bool(second_input) == True)
    52    # ):
    53    if (not bool(first_input)) and (not bool(second_input)):
    54            return False
    55    return True
    

    still green

  • I use a simpler if statement

    47def logical_disjunction(first_input, second_input):
    48    # if (not bool(first_input)) and (not bool(second_input)):
    49    if (not first_input) and (not second_input):
    50            return False
    51    return True
    

    how many green bottles standing on a wall?

  • I add a second if statement for the opposite (logical negation) of the statement I have, like I did with Logical NAND

    47def logical_disjunction(first_input, second_input):
    48    if (not first_input) and (not second_input):
    49            return False
    50    if not ((not first_input) and (not second_input)):
    51        return True
    

    the test is still green, that is a lot of nots

  • I move the new if statement to the top

    47def logical_disjunction(first_input, second_input):
    48    if not ((not first_input) and (not second_input)):
    49        return True
    50    if (not first_input) and (not second_input):
    51            return False
    

    still green

  • I change the second if statement to an else clause

    47def logical_disjunction(first_input, second_input):
    48    if not ((not first_input) and (not second_input)):
    49        return True
    50    # if (not first_input) and (not second_input):
    51    else:
    52            return False
    

    green

  • I add a conditional expression

    47def logical_disjunction(first_input, second_input):
    48    return True if not (
    49        (not first_input) and (not second_input)
    50    ) else False
    51    if not ((not first_input) and (not second_input)):
    52        return True
    53    else:
    54            return False
    

    green again 🎶

  • I make the return statement simpler

    47def logical_disjunction(first_input, second_input):
    48    return not ((not first_input) and (not second_input))
    49    return True if not (
    50        (not first_input) and (not second_input)
    51    ) else False
    

    the test is still green. A reminder that I can return the Logical Negation (not) of an if statement that returns False like I did with Logical NAND

    Note

    return not ((not first_input) and (not second_input)) returns the opposite (Logical Negation) of the Logical Conjunction of the Logical Negation of first_input, and the Logical Negation of second_input. This means that in the 4 cases

    • if the first input is True and the second input is True, logical_disjunction returns

      not ((not first_input) and (not second_input))
      not ((not True)        and (not True))
      not (False             and False)
      not False
      True
      
    • if the first input is True and the second input is False, logical_disjunction returns

      not ((not first_input) and (not second_input))
      not ((not True)        and (not False))
      not (False             and True)
      not False
      True
      
    • if the first input is False and the second input is True, logical_disjunction returns

      not ((not first_input) and (not second_input))
      not ((not False)       and (not True))
      not (True              and False)
      not False
      True
      
    • if the first input is False and the second input is False, logical_disjunction returns

      not ((not first_input) and (not second_input))
      not ((not False)       and (not False))
      not (True              and True)
      not True
      False
      
  • not” appears 3 times in this statement, I want to change that. I “multiply” it by each thing inside the parentheses to try to make the statement simpler

    47def logical_disjunction(first_input, second_input):
    48    return (not not first_input) (not and) (not not second_input)
    49    return not ((not first_input) and (not second_input))
    

    the terminal shows SyntaxError

    SyntaxError: invalid syntax
    
  • I add SyntaxError to the list of Exceptions seen in test_binary.py

     97# Exceptions seen
     98# AttributeError
     99# TypeError
    100# AssertionError
    101# SyntaxError
    
  • I fix the failing line by changing “not and” to “or” in truth_table.py

    47def logical_disjunction(first_input, second_input):
    48    return (not not first_input) or (not not second_input)
    49    return not ((not first_input) and (not second_input))
    

    the test passes

  • I remove not not from the return statement because it cancels out

    47def logical_disjunction(first_input, second_input):
    48    return first_input or second_input
    49    return (not not first_input) or (not not second_input)
    

    the test is still green. Do two nots make a right?

  • I remove the second return statement

    47def logical_disjunction(first_input, second_input):
    48    return first_input or second_input
    

    green everywhere

Logical Disjunction also known as “or” returns

  • first_input or second_input

  • False, if the first input is False and the second input is False


close the project

  • I close test_binary.py and truth_table.py in the editor

  • I click in the terminal, then use q on the keyboard to leave the tests. The terminal goes back to the command line

  • I change directory to the parent of truth_table

    cd ..
    

    the terminal shows

    .../pumping_python
    

    I am back in the pumping_python directory


review

Binary Operations take 2 inputs, each input can be True or False, if the first input is named first_input and the second input is named second_input, the tests show that

and

All the logic statements or conditions have been written with some or all of these 3.


code from the chapter

Do you want to see all the CODE I typed for the Truth Table?


what is next?

Would you like test even more binary operations?


rate pumping python

If this has been a 7 star experience for you, please CLICK HERE to leave a 5 star review of pumping python. It helps other people get into the book too