family ties

In object oriented programming there is a concept called Inheritance, it allows me to define new objects that get their magic powers from other objects.

Making new objects can be easier with Inheritance because I do not have to rewrite things that have already been written, I can inherit them instead and change the new objects to do what I want.

It can also be more complicated because I can make new instances to inherit from one class and customize it for what I need instead of making new classes that require me to keep track of Python’s Method Resolution Order.


how to make a class with a parent

To use inheritance I put the “parent” in parentheses when I define the new object (the child) to make the relationship

class Child(Parent):

    attribute = SOMETHING

    def method():
        the body of the method
        ...

preview

I have these tests by the end of the chapter

  1import src.classes
  2import unittest
  3
  4
  5class TestClasses(unittest.TestCase):
  6
  7    def test_making_a_class_w_pass(self):
  8        an_instance = src.classes.WPass()
  9        assert isinstance(an_instance, object)
 10        self.assertIsInstance(an_instance, object)
 11
 12    def test_making_a_class_w_parentheses(self):
 13        an_instance = src.classes.WParentheses()
 14        assert isinstance(an_instance, object)
 15        self.assertIsInstance(an_instance, object)
 16
 17    def test_making_a_class_w_object(self):
 18        an_instance = src.classes.WObject()
 19        assert isinstance(an_instance, object)
 20        self.assertIsInstance(an_instance, object)
 21
 22    def test_is_none_an_object(self):
 23        assert isinstance(None, object)
 24        self.assertIsInstance(None, object)
 25
 26    def test_is_a_boolean_an_object(self):
 27        assert issubclass(bool, object)
 28        self.assertIsSubclass(bool, object)
 29
 30    def test_is_an_integer_an_object(self):
 31        assert issubclass(int, object)
 32        self.assertIsSubclass(int, object)
 33
 34    def test_is_a_float_an_object(self):
 35        assert issubclass(float, object)
 36        self.assertIsSubclass(float, object)
 37
 38    def test_is_a_string_an_object(self):
 39        assert issubclass(str, object)
 40        self.assertIsSubclass(str, object)
 41
 42    def test_is_a_tuple_an_object(self):
 43        assert issubclass(tuple, object)
 44        self.assertIsSubclass(tuple, object)
 45
 46    def test_is_a_list_an_object(self):
 47        assert issubclass(list, object)
 48        self.assertIsSubclass(list, object)
 49
 50    def test_is_a_set_an_object(self):
 51        assert issubclass(set, object)
 52        self.assertIsSubclass(set, object)
 53
 54    def test_is_a_dictionary_an_object(self):
 55        assert issubclass(dict, object)
 56        self.assertIsSubclass(dict, object)
 57
 58    def test_attributes_and_methods_of_objects(self):
 59        reality = dir(object)
 60        my_expectation = [
 61            '__class__',
 62            '__delattr__',
 63            '__dir__',
 64            '__doc__',
 65            '__eq__',
 66            '__format__',
 67            '__ge__',
 68            '__getattribute__',
 69            '__getstate__',
 70            '__gt__',
 71            '__hash__',
 72            '__init__',
 73            '__init_subclass__',
 74            '__le__',
 75            '__lt__',
 76            '__ne__',
 77            '__new__',
 78            '__reduce__',
 79            '__reduce_ex__',
 80            '__repr__',
 81            '__setattr__',
 82            '__sizeof__',
 83            '__str__',
 84            '__subclasshook__'
 85        ]
 86        self.assertEqual(reality, my_expectation)
 87
 88    def test_making_a_class_w_inheritance(self):
 89        a_class = src.classes.Doe
 90
 91        assert not isinstance(a_class, src.person.Person)
 92        self.assertNotIsInstance(
 93            a_class, src.person.Person
 94        )
 95
 96        assert issubclass(a_class, src.person.Person)
 97        self.assertIsSubclass(
 98            a_class, src.person.Person
 99        )
100
101        assert not isinstance(a_class, a_class)
102        self.assertNotIsInstance(a_class, a_class)
103
104        an_instance = src.classes.Doe('first_name')
105        assert isinstance(
106            an_instance, src.person.Person
107        )
108        self.assertIsInstance(
109            an_instance, src.person.Person
110        )
111
112        self.assertEqual(
113            dir(a_class),
114            dir(src.person.Person)
115        )
116
117    def test_classes_w_one_parent(self):
118        doe = src.classes.Doe('doe')
119        self.assertEqual(doe.last_name, 'doe')
120
121        joe = src.classes.Blow('joe')
122        self.assertEqual(joe.last_name, 'blow')
123
124        blow = src.person.Person('joe', last_name='blow')
125        self.assertEqual(blow.last_name, joe.last_name)
126
127        jane = src.person.Person('jane')
128        self.assertEqual(jane.last_name, doe.last_name)
129
130        john = src.classes.Smith('john')
131        self.assertEqual(john.last_name, 'smith')
132
133        smith = src.person.Person('john', 'smith')
134        self.assertEqual(smith.last_name, john.last_name)
135
136    def test_classes_w_multiple_parents(self):
137        joe = src.classes.Joe()
138        self.assertEqual(joe.first_name, 'joe')
139        self.assertEqual(joe.last_name, 'blow')
140        assert issubclass(
141            src.classes.Joe, src.classes.Blow
142        )
143        # self.assertNotIsSubclass(
144        self.assertIsSubclass(
145            src.classes.Joe, src.classes.Blow
146        )
147
148        jane = src.classes.Jane()
149        self.assertEqual(jane.first_name, 'jane')
150        self.assertEqual(jane.last_name, 'doe')
151        self.assertEqual(jane.eye_color, 'brown')
152        self.assertIsSubclass(
153            src.classes.Jane, src.classes.Doe
154        )
155
156        # mary = src.classes.Jane('mary')
157        mary = src.classes.Mary()
158        self.assertEqual(mary.first_name, 'mary')
159        # self.assertEqual(mary.last_name, mary.first_name)
160        # self.assertEqual(mary.last_name, jane.last_name)
161        self.assertEqual(mary.last_name, joe.last_name)
162        # self.assertEqual(mary.eye_color, jane.eye_color)
163        self.assertEqual(mary.eye_color, 'red')
164        # assert not issubclass(
165        assert issubclass(
166            src.classes.Mary, src.classes.Jane
167        )
168        # self.assertNotIsSubclass(
169        self.assertIsSubclass(
170            src.classes.Mary, src.classes.Jane
171        )
172        assert issubclass(
173            src.classes.Mary, src.classes.Joe
174        )
175        # self.assertNotIsSubclass(
176        self.assertIsSubclass(
177            src.classes.Mary, src.classes.Joe
178        )
179
180        john = src.classes.John()
181        self.assertEqual(john.first_name, 'john')
182        self.assertEqual(john.last_name, 'smith')
183        self.assertEqual(john.eye_color, 'orange')
184        assert issubclass(
185            src.classes.John, src.classes.Smith
186        )
187        # self.assertNotIsSubclass(
188        self.assertIsSubclass(
189            src.classes.John, src.classes.Smith
190        )
191
192        lil = src.classes.Lil()
193        self.assertEqual(lil.first_name, 'lil')
194        self.assertEqual(lil.last_name, mary.last_name)
195        # self.assertEqual(lil.last_name, john.last_name)
196        # self.assertEqual(lil.eye_color, '')
197        # self.assertEqual(lil.eye_color, jane.eye_color)
198        self.assertEqual(lil.eye_color, mary.eye_color)
199        assert issubclass(
200            src.classes.Lil, src.classes.John
201        )
202        # self.assertNotIsSubclass(
203        self.assertIsSubclass(
204            src.classes.Lil, src.classes.John
205        )
206        assert issubclass(
207            src.classes.Lil, src.classes.Mary
208        )
209
210
211# Exceptions seen
212# AssertionError
213# NameError
214# AttributeError
215# ModuleNotFoundError
216# TypeError

requirements


open the project

  • I change directory to the person folder

    cd person
    

    the terminal shows I am in the person folder

    .../pumping_python/person
    
  • I make a new file in the tests folder named test_classes.py

    touch tests/test_classes.py
    
  • I make a new file in the src folder named classes.py

    touch src/classes.py
    
  • I open test_classes.py in the editor of the Integrated Development Environment (IDE)

    Tip

    I can open a file from the terminal in the Integrated Development Environment (IDE) with the name of the program and the name of the file. That means if I type this in the terminal

    code tests/test_classes.py
    

    Visual Studio Code opens test_classes.py in the editor

  • I add the first failing test to test_functions.py

    1import unittest
    2
    3
    4class TestClasses(unittest.TestCase):
    5
    6    def test_failure(self):
    7        self.assertFalse(True)
    
  • I go back to the terminal to add the new files and folders to git for tracking

    git add .
    

    the terminal goes back to the command line.

  • I use pytest-watcher to run the tests

    uv run pytest-watcher . --now
    

    the terminal is my friend, and shows AssertionError

    ================================ FAILURES ==============================
    _______________________ TestClasses.test_failure _______________________
    
    self = <tests.test_classes.TestClasses testMethod=test_failure>
    
        def test_failure(self):
    >       self.assertFalse(True)
    E       AssertionError: True is not false
    
    tests/test_classes.py:7: AssertionError
    ======================== short test summary info =========================
    FAILED tests/test_classes.py::TestClasses::test_failure - AssertionError: True is not false
    =========================== 1 failed in X.YZs ============================
    
  • I add AssertionError to the list of Exceptions seen in test_functions.py in the editor

     4class TestFunctions(unittest.TestCase):
     5
     6    def test_failure(self):
     7        self.assertFalse(True)
     8
     9
    10# Exceptions seen
    11# AssertionError
    
  • then I change True to False in the assertion

    7        self.assertFalse(False)
    

    the test passes.


test_making_a_class_w_pass

To review, I can make a class with the class keyword, use CapWords format for the name and use a name that tells what the group of attributes and methods do.


RED: make it fail


how to test if something is NOT an instance of a class


I can test if an object is an instance (a copy) of another object or NOT with the `isinstance built-in function`_ from The Python Standard Library, it checks if the thing in the parentheses on the left is an instance of the class on the right in the parentheses

  • I change test_failure to test_making_a_class_w_pass then add an assertion with isinstance_

     4class TestClasses(unittest.TestCase):
     5
     6    def test_making_a_class_w_pass(self):
     7        assert not isinstance(
     8            src.classes.WPass(), object
     9        )
    10
    11
    12# Exceptions seen
    

    the terminal is my friend, and shows NameError

    NameError: name 'src' is not defined
    

    because src is not defined in this file

  • I add NameError to the list of Exceptions seen

    12# Exceptions seen
    13# AssertionError
    14# NameError
    

GREEN: make it pass


  • I add an import statement for the classes module

    1import src.classes
    2import unittest
    3
    4
    5class TestClasses(unittest.TestCase):
    
    • import src.classes brings in an object that represents the classes.py module from the src folder so I can use it in test_classes.py

    • the terminal is my friend, and shows AttributeError

      AttributeError: module 'src.classes'
                      has no attribute 'WPass'
      

      because there is no definition for WPass in classes.py

  • I add AttributeError to the list of Exceptions seen

    13# Exceptions seen
    14# AssertionError
    15# NameError
    16# AttributeError
    
  • I use the Explorer to open classes.py from the src folder in the editor

  • then I add a class definition for WPass to classes.py

    1class WPass: pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert not True
    

    because the statement not isinstance(src.classes.WPass(), object) is False


how to test if something is an instance of a class



REFACTOR: make it better


The unittest.TestCase class has 2 methods I can also use to test if an object is an instance (a copy) of a class or NOT - assertIsInstance_ and assertNotIsInstance_


another way to test if something is NOT an instance of a class


I add the `assertNotIsInstance method`_ to the test

 7    def test_making_a_class_w_pass(self):
 8        # assert not isinstance(
 9        assert isinstance(
10            src.classes.WPass(), object
11        )
12        self.assertNotIsInstance(
13            src.classes.WPass(), object
14        )
15
16
17# Exceptions seen

the terminal is my friend, and shows AssertionError

AssertionError:
    <src.classes.WPass() object at 0xffff8a7b6543>
    is an instance of <class 'object'>

another way to test if something is an instance of a class


  • I change assertNotIsInstance_ to assertIsInstance_

     7    def test_making_a_class_w_pass(self):
     8        # assert not isinstance(
     9        assert isinstance(
    10            src.classes.WPass(), object
    11        )
    12        # self.assertNotIsInstance(
    13        self.assertIsInstance(
    14            src.classes.WPass(), object
    15        )
    16
    17
    18# Exceptions seen
    

    the test passes.

  • I add a variable to use to remove repetition of src.classes.WPass()

     7    def test_making_a_class_w_pass(self):
     8        an_instance = src.classes.WPass()
     9        # assert not isinstance(
    10        assert isinstance(
    11            src.classes.WPass(), object
    12        )
    13        # self.assertNotIsInstance(
    14        self.assertIsInstance(
    15            src.classes.WPass(), object
    16        )
    17
    18
    19# Exceptions seen
    
  • I use the variable to remove repetition of src.classes.WPass()

     7    def test_making_a_class_w_pass(self):
     8        an_instance = src.classes.WPass()
     9        # assert not isinstance(
    10        assert isinstance(an_instance, object)
    11        #     src.classes.WPass(), object
    12        # )
    13        # self.assertNotIsInstance(
    14        self.assertIsInstance(an_instance, object)
    15        #     src.classes.WPass(), object
    16        # )
    17
    18
    19# Exceptions seen
    
  • I remove the commented lines

     7    def test_making_a_class_w_pass(self):
     8        an_instance = src.classes.WPass()
     9        assert isinstance(an_instance, object)
    10        self.assertIsInstance(an_instance, object)
    11
    12
    13# Exceptions seen
    
  • I open a new terminal, then add a git commit message

    git commit -am \
    'add test_making_a_class_w_pass'
    

I can make a class with pass


test_making_a_class_w_parentheses

I can also make a class with parentheses/brackets ( ).


RED: make it red


  • I go back to the terminal that is running the tests

  • I add another test

     7    def test_making_a_class_w_pass(self):
     8        an_instance = src.classes.WPass()
     9        assert isinstance(an_instance, object)
    10        self.assertIsInstance(an_instance, object)
    11
    12    def test_making_a_class_w_parentheses(self):
    13        assert not isinstance(
    14            src.classes.WParentheses(), object
    15        )
    16
    17
    18# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.classes'
                    has no attribute 'WParentheses'
    

GREEN: make it pass


  • I add a class definition like WPass to classes.py

    1class WPass: pass
    2
    3
    4class WParentheses: pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert not True
    

    because the statement not isinstance(src.classes.WParentheses(), object) is False

  • I change the assertion to make the statement True

    12    def test_making_a_class_w_parentheses(self):
    13        # assert not isinstance(
    14        assert isinstance(
    15            src.classes.WParentheses(), object
    16        )
    17
    18
    19# Exceptions seen
    

    the test passes.


REFACTOR: make it better


  • I add parentheses to the definition

    4# class WParentheses: pass
    5class WParentheses(): pass
    
  • I remove the commented line

    1class WPass: pass
    2
    3
    4class WParentheses(): pass
    
  • I add the `assertNotIsInstance method`_ to test_making_a_class_w_parentheses in test_classes.py

    12    def test_making_a_class_w_parentheses(self):
    13        # assert not isinstance(
    14        assert isinstance(
    15            src.classes.WParentheses(), object
    16        )
    17        self.assertNotIsInstance(
    18            src.classes.WParentheses(), object
    19        )
    20
    21
    22# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <src.classes.WParentheses() object at 0xffffab123456>
        is an instance of <class 'object'>
    
  • I change assertNotIsInstance_ to assertIsInstance_

    12    def test_making_a_class_w_parentheses(self):
    13        # assert not isinstance(
    14        assert isinstance(
    15            src.classes.WParentheses(), object
    16        )
    17        # self.assertNotIsInstance(
    18        self.assertIsInstance(
    19            src.classes.WParentheses(), object
    20        )
    21
    22
    23# Exceptions seen
    

    the test passes.

  • I add a variable to use to remove repetition of src.classes.WParentheses()

    12    def test_making_a_class_w_parentheses(self):
    13        an_instance = src.classes.WParentheses()
    14        # assert not isinstance(
    15        assert isinstance(
    16            src.classes.WParentheses(), object
    17        )
    18        # self.assertNotIsInstance(
    19        self.assertIsInstance(
    20            src.classes.WParentheses(), object
    21        )
    22
    23
    24# Exceptions seen
    
  • I use the variable to remove repetition of src.classes.WParentheses()

    12    def test_making_a_class_w_parentheses(self):
    13        an_instance = src.classes.WParentheses()
    14        # assert not isinstance(
    15        assert isinstance(an_instance, object)
    16        #     src.classes.WParentheses(), object
    17        # )
    18        # self.assertNotIsInstance(
    19        self.assertIsInstance(an_instance, object)
    20        #     src.classes.WParentheses(), object
    21        # )
    22
    23
    24# Exceptions seen
    
  • I remove the commented lines

    12    def test_making_a_class_w_parentheses(self):
    13        an_instance = src.classes.WParentheses()
    14        assert isinstance(an_instance, object)
    15        self.assertIsInstance(an_instance, object)
    16
    17
    18# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am \
    'add test_making_a_class_w_parentheses'
    

I can make a class with parentheses

I have two classes with different statements, and the tests show that they are both instances of the object class

class WPass: pass
class WParentheses(): pass

because “all classes inherit from ‘object’”, which leads me to the next test.


test_making_a_class_w_object

I can make a class with object (the mother of all classes).


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test with an assertion for a new class

    12    def test_making_a_class_w_parentheses(self):
    13        an_instance = src.classes.WParentheses()
    14        assert isinstance(an_instance, object)
    15        self.assertIsInstance(an_instance, object)
    16
    17    def test_making_a_class_w_object(self):
    18        assert not isinstance(
    19            src.classes.WObject(), object
    20        )
    21
    22
    23# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.classes' has no attribute 'WObject'
    

GREEN: make it pass


  • I add the class definition to classes.py

    4class WParentheses(): pass
    5
    6
    7class WObject(): pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert not True
    
  • I change the statement of the assertion in test_making_a_class_w_object to make it True

    17    def test_making_a_class_w_object(self):
    18        # assert not isinstance(
    19        assert isinstance(
    20            src.classes.WObject(), object
    21        )
    22
    23
    24# Exceptions seen
    

    the test passes.


REFACTOR: make it better


  • I add object to the parentheses of the class definition for WObject in classes.py

    7# class WObject(): pass
    8class WObject(object): pass
    

    the test is still green.

  • I remove the commented line

    4class WParentheses(): pass
    5
    6
    7class WObject(object): pass
    
  • I add an assertion with the `assertNotIsInstance method`_ to test_making_a_class_w_object in test_classes.py

    17    def test_making_a_class_w_object(self):
    18        # assert not isinstance(
    19        assert isinstance(
    20            src.classes.WObject(), object
    21        )
    22        self.assertNotIsInstance(
    23            src.classes.WObject(), object
    24        )
    25
    26
    27# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <src.classes.WObject() object at 0xffffcd781234>
        is an instance of <class 'object'>
    
  • I change assertNotIsInstance_ to assertIsInstance_

    17    def test_making_a_class_w_object(self):
    18        # assert not isinstance(
    19        assert isinstance(
    20            src.classes.WObject(), object
    21        )
    22        # self.assertNotIsInstance(
    23        self.assertIsInstance(
    24            src.classes.WObject(), object
    25        )
    26
    27
    28# Exceptions seen
    

    the test passes.

  • I add a variable to remove repetition of src.classes.WObject()

    17    def test_making_a_class_w_object(self):
    18        an_instance = src.classes.WObject()
    19        # assert not isinstance(
    20        assert isinstance(
    21            src.classes.WObject(), object
    22        )
    23        # self.assertNotIsInstance(
    24        self.assertIsInstance(
    25            src.classes.WObject(), object
    26        )
    27
    28
    29# Exceptions seen
    
  • I use the variable to remove repetition of src.classes.WObject()

    17    def test_making_a_class_w_object(self):
    18        an_instance = src.classes.WObject()
    19        # assert not isinstance(
    20        assert isinstance(an_instance, object)
    21        #     src.classes.WObject(), object
    22        # )
    23        # self.assertNotIsInstance(
    24        self.assertIsInstance(an_instance, object)
    25        #     src.classes.WObject(), object
    26        # )
    27
    28
    29# Exceptions seen
    

    the test is still green.

  • I remove the commented lines

    17    def test_making_a_class_w_object(self):
    18        an_instance = src.classes.WObject()
    19        assert isinstance(an_instance, object)
    20        self.assertIsInstance(an_instance, object)
    21
    22
    23# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_making_a_class_w_object'
    

I have three different classes, and the tests show that they are all instances of the object class

class WPass: pass
class WParentheses(): pass
class WObject(object): pass

their definitions are different, their results are the same because “all classes inherit from ‘object’

I like to write my classes with (object), so that anyone can see what the parent class is without thinking about it.

I can make a class with object


test_is_none_an_object

I want to test if None is an object.


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test with an assertion

    17    def test_making_a_class_w_object(self):
    18        an_instance = src.classes.WObject()
    19        assert isinstance(an_instance, object)
    20        self.assertIsInstance(an_instance, object)
    21
    22    def test_is_none_an_object(self):
    23        assert not isinstance(None, object)
    24
    25
    26# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    E       assert not True
    

    because None is an object.


GREEN: make it pass


I change the statement to make it True

22    def test_is_none_an_object(self):
23        # assert not isinstance(None, object)
24        assert isinstance(None, object)
25
26
27# Exceptions seen

the test passes.


REFACTOR: make it better


  • I use assertNotIsInstance_ to show that None is a child of object

    22    def test_is_none_an_object(self):
    23        # assert not isinstance(None, object)
    24        assert isinstance(None, object)
    25        self.assertNotIsInstance(None, object)
    26
    27
    28# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: None is an instance of <class 'object'>
    

    because None is an object.

  • I change assertNotIsInstance_ to assertIsInstance_

    22    def test_is_none_an_object(self):
    23        # assert not isinstance(None, object)
    24        assert isinstance(None, object)
    25        # self.assertNotIsInstance(None, object)
    26        self.assertIsInstance(None, object)
    27
    28
    29# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    22    def test_is_none_an_object(self):
    23        assert isinstance(None, object)
    24        self.assertIsInstance(None, object)
    25
    26
    27# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_is_none_an_object'
    

test_is_a_boolean_an_object

I want to test if a boolean is an object.


RED: make it fail


how to test if something is NOT a subclass of a class


I can test if an object is a subclass (child) of another object or NOT with the `issubclass built-in function`_ from The Python Standard Library, it checks if the thing in the parentheses on the left is a subclass of the class on the right in the parentheses


GREEN: make it pass


how to test if something is a subclass of a class


I change the statement to make it True

26    def test_is_a_boolean_an_object(self):
27        # assert not issubclass(bool, object)
28        assert issubclass(bool, object)
29
30
31# Exceptions seen

the test passes.


REFACTOR: make it better


The unittest.TestCase class has 2 methods I can also use to test if an object is a subclass (child) of a class or NOT - assertIsSubclass_ and assertNotIsSubclass_


another way to test if something is NOT a subclass of a class


I use `unittest.TestCase.assertNotIsSubclass`_ to show that bool (the class for booleans) is a child of object

26    def test_is_a_boolean_an_object(self):
27        # assert not issubclass(bool, object)
28        assert issubclass(bool, object)
29        self.assertNotIsSubclass(bool, object)
30
31
32# Exceptions seen

the terminal is my friend, and shows AssertionError

AssertionError:
    <class 'bool'> is a subclass of <class 'object'>

because bool is a child of object.


another way to test if something is a subclass of a class


  • I change assertNotIsSubclass_ to assertIsSubclass_

    26    def test_is_a_boolean_an_object(self):
    27        # assert not issubclass(bool, object)
    28        assert issubclass(bool, object)
    29        # self.assertNotIsSubclass(bool, object)
    30        self.assertIsSubclass(bool, object)
    31
    32
    33# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    26    def test_is_a_boolean_an_object(self):
    27        assert issubclass(bool, object)
    28        self.assertIsSubclass(bool, object)
    29
    30
    31# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am \
    'add test_is_a_boolean_an_object'
    

A boolean is an object.


test_is_an_integer_an_object

I want to test if an integer (a whole number without decimals) is an object


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test with an assertion for int (the class for whole numbers without decimals), to show that everything in Python is a child of object.

    26    def test_is_a_boolean_an_object(self):
    27        assert issubclass(bool, object)
    28        self.assertIsSubclass(bool, object)
    29
    30    def test_is_an_integer_an_object(self):
    31        assert not issubclass(int, object)
    32
    33
    34# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    E       assert not True
    

    because int is a child of object.


GREEN: make it pass


I change the statement to make it True

30    def test_is_an_integer_an_object(self):
31        # assert not issubclass(int, object)
32        assert issubclass(int, object)
33
34
35# Exceptions seen

the test passes.


REFACTOR: make it better


  • I use assertNotIsSubclass_ to show that int is a child of object

    30    def test_is_an_integer_an_object(self):
    31        # assert not issubclass(int, object)
    32        assert issubclass(int, object)
    33        self.assertNotIsSubclass(int, object)
    34
    35
    36# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'int'> is a subclass of <class 'object'>
    

    because int is a child of object.

  • I change assertNotIsSubclass_ to assertIsSubclass_

    43    def test_is_an_integer_an_object(self):
    44        # assert not issubclass(int, object)
    45        assert issubclass(int, object)
    46        # self.assertNotIsSubclass(int, object)
    47        self.assertIsSubclass(int, object)
    48
    49
    50# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    30    def test_is_an_integer_an_object(self):
    31        assert issubclass(int, object)
    32        self.assertIsSubclass(int, object)
    33
    34
    35# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am \
    'add test_is_an_integer_an_object'
    

An integer is an object.


test_is_a_float_an_object

I want to test if a float (a binary floating point decimal number) is an object


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test with an assertion for float (the class for binary floating point decimal numbers), to show that everything in Python is a child of object.

    30    def test_is_an_integer_an_object(self):
    31        assert issubclass(int, object)
    32        self.assertIsSubclass(int, object)
    33
    34    def test_is_a_float_an_object(self):
    35        assert not issubclass(float, object)
    36
    37
    38# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    E       assert not True
    

    because float is a child of object.


GREEN: make it pass


I change the statement to make it True

34    def test_is_a_float_an_object(self):
35        # assert not issubclass(float, object)
36        assert issubclass(float, object)
37
38
39# Exceptions seen

the test passes.


REFACTOR: make it better


  • I use assertNotIsSubclass_ to show that float is a child of object

    34    def test_is_a_float_an_object(self):
    35        # assert not issubclass(float, object)
    36        assert issubclass(float, object)
    37        self.assertNotIsSubclass(float, object)
    38
    39
    40# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'float'> is a subclass of <class 'object'>
    

    because float is a child of object.

  • I change assertNotIsSubclass_ to assertIsSubclass_

    34    def test_is_a_float_an_object(self):
    35        # assert not issubclass(float, object)
    36        assert issubclass(float, object)
    37        # self.assertNotIsSubclass(float, object)
    38        self.assertIsSubclass(float, object)
    39
    40
    41# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    34    def test_is_a_float_an_object(self):
    35        assert issubclass(float, object)
    36        self.assertIsSubclass(float, object)
    37
    38
    39# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_is_a_float_an_object'
    

A float is an object.


test_is_a_string_an_object

I want to test if a string (anything in quotes) is an object.


RED: make it fail



GREEN: make it pass


I change the statement to make it True

38    def test_is_a_string_an_object(self):
39        # assert not issubclass(str, object)
40        assert issubclass(str, object)
41
42
43# Exceptions seen

the test passes.


REFACTOR: make it better


  • I use assertNotIsSubclass_ to show that str is a child of object

    38    def test_is_a_string_an_object(self):
    39        # assert not issubclass(str, object)
    40        assert issubclass(str, object)
    41        self.assertNotIsSubclass(str, object)
    42
    43
    44# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'str'> is a subclass of <class 'object'>
    

    because str is a child of object.

  • I change assertNotIsInstance_ to assertIsInstance_

    38    def test_is_a_string_an_object(self):
    39        # assert not issubclass(str, object)
    40        assert issubclass(str, object)
    41        # self.assertNotIsSubclass(str, object)
    42        self.assertIsSubclass(str, object)
    43
    44
    45# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    38    def test_is_a_string_an_object(self):
    39        assert issubclass(str, object)
    40        self.assertIsSubclass(str, object)
    41
    42
    43# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_is_a_string_an_object'
    

A float is an object.


test_is_a_tuple_an_object

I want to test if a tuple (anything in parentheses ( ) separated by a comma) is an object.


RED: make it fail



GREEN: make it pass


I change the statement to make it True

42    def test_is_a_tuple_an_object(self):
43        # assert not issubclass(tuple, object)
44        assert issubclass(tuple, object)
45
46
47# Exceptions seen

the test passes.


REFACTOR: make it better


  • I use assertNotIsSubclass_ to show that tuple is a child of object

    42    def test_is_a_tuple_an_object(self):
    43        # assert not issubclass(tuple, object)
    44        assert issubclass(tuple, object)
    45        self.assertNotIsSubclass(tuple, object)
    46
    47
    48# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'tuple'> is a subclass of <class 'object'>
    

    because tuple is a child of object.

  • I change assertNotIsSubclass_ to assertIsSubclass_

    42    def test_is_a_tuple_an_object(self):
    43        # assert not issubclass(tuple, object)
    44        assert issubclass(tuple, object)
    45        # self.assertNotIsSubclass(tuple, object)
    46        self.assertIsSubclass(tuple, object)
    47
    48
    49# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    42    def test_is_a_tuple_an_object(self):
    43        assert issubclass(tuple, object)
    44        self.assertIsSubclass(tuple, object)
    45
    46
    47# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_is_a_tuple_an_object'
    

A tuple is an object.


test_is_a_list_an_object

I want to test if a list (anything in square brackets [ ]) is an object


RED: make it fail



GREEN: make it pass


I change the statement to make it True

46      def test_is_a_list_an_object(self):
47          # assert not issubclass(list, object)
48          assert issubclass(list, object)
49
50
51  # Exceptions seen

the test passes.


REFACTOR: make it better


  • I use assertNotIsSubclass_ to show that list is a child of object

    46    def test_is_a_list_an_object(self):
    47        # assert not issubclass(list, object)
    48        assert issubclass(list, object)
    49        self.assertNotIsSubclass(list, object)
    50
    51
    52# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'list'> is a subclass of <class 'object'>
    

    because list is a child of object.

  • I change assertNotIsSubclass_ to assertIsSubclass_

    46    def test_is_a_list_an_object(self):
    47        # assert not issubclass(list, object)
    48        assert issubclass(list, object)
    49        # self.assertNotIsSubclass(list, object)
    50        self.assertIsSubclass(list, object)
    51
    52
    53# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    46    def test_is_a_list_an_object(self):
    47        assert issubclass(list, object)
    48        self.assertIsSubclass(list, object)
    49
    50
    51# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_is_a_list_an_object'
    

A list is an object.


test_is_a_set_an_object

I want to test if a set (anything in curly braces { } separated by a comma) is an object.


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test with an assertion for set (the class for anything in curly braces { } separated by a comma), to show that everything in Python is a child of object

    46    def test_is_a_list_an_object(self):
    47        assert issubclass(list, object)
    48        self.assertIsSubclass(list, object)
    49
    50    def test_is_a_set_an_object(self):
    51        assert not issubclass(set, object)
    52
    53
    54# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    E       assert not True
    

    because set is a child of object.


GREEN: make it pass


I change the statement to make it True

50    def test_is_a_set_an_object(self):
51        # assert not issubclass(set, object)
52        assert issubclass(set, object)
53
54
55# Exceptions seen

the test passes.


REFACTOR: make it better


  • I use assertNotIsSubclass_ to show that set is a child of object

    50    def test_is_a_set_an_object(self):
    51        # assert not issubclass(set, object)
    52        assert issubclass(set, object)
    53        self.assertNotIsSubclass(set, object)
    54
    55
    56# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'set'> is a subclass of <class 'object'>
    

    because set is a child of object.

  • I change assertNotIsSubclass_ to assertIsSubclass_

    50    def test_is_a_set_an_object(self):
    51        # assert not issubclass(set, object)
    52        assert issubclass(set, object)
    53        # self.assertNotIsSubclass(set, object)
    54        self.assertIsSubclass(set, object)
    55
    56
    57# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    50    def test_is_a_set_an_object(self):
    51        assert issubclass(set, object)
    52        self.assertIsSubclass(set, object)
    53
    54
    55# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am 'add test_is_a_set_an_object'
    

A set is an object.


test_is_a_dictionary_an_object

I want to test if a dictionary (any key-value pairs in curly braces { } separated by a comma) is an object.



RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test with an assertion for dict (the class for any key-value pairs in curly braces ‘{ }’ separated by a comma), to show that everything in Python is a child of object

    50    def test_is_a_set_an_object(self):
    51        assert issubclass(set, object)
    52        self.assertIsSubclass(set, object)
    53
    54    def test_is_a_dictionary_an_object(self):
    55        assert not issubclass(dict, object)
    56
    57
    58# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    E       assert not True
    

    because dict is a child of object.

  • I change the statement to make it True

    54    def test_is_a_dictionary_an_object(self):
    55        # assert not issubclass(dict, object)
    56        assert issubclass(dict, object)
    57
    58
    59# Exceptions seen
    

    the test passes.

  • I use assertNotIsSubclass_ to show that dict is a child of object

    54    def test_is_a_dictionary_an_object(self):
    55        # assert not issubclass(dict, object)
    56        assert issubclass(dict, object)
    57        self.assertNotIsSubclass(dict, object)
    58
    59
    60# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'dict'> is a subclass of <class 'object'>
    

    because dict is a child of object.

  • I change assertNotIsSubclass_ to assertIsSubclass_

    54        # assert not isinstance(dict, object)
    55        assert isinstance(dict, object)
    56        # self.assertNotIsInstance(dict, object)
    57        self.assertIsInstance(dict, object)
    58
    59
    60# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    54    def test_is_a_dictionary_an_object(self):
    55        assert issubclass(dict, object)
    56        self.assertIsSubclass(dict, object)
    57
    58
    59# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am \
    'add test_is_a_dictionary_an_object'
    

A dictionary is an object.


test_attributes_and_methods_of_objects

In test_attributes_and_methods_of_person_class I saw the methods I added to the Person class and also names that I did not add, which led to the question of where they came from.

I want to test the attributes and methods of the object class.


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test to test_classes.py

    54    def test_is_a_dictionary_an_object(self):
    55        assert issubclass(dict, object)
    56        self.assertIsSubclass(dict, object)
    57
    58    def test_attributes_and_methods_of_objects(self):
    59        reality = dir(object)
    60        my_expectation = []
    61        self.assertEqual(reality, my_expectation)
    62
    63
    64# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: Lists differ:
        ['__class__', '__delattr__', '__dir__', '_[272 chars]k__']
     != []
    

GREEN: make it pass


  • I copy (ctrl/command+c) the values from the terminal and paste (ctrl/command+v) them as my_expectation

    58    def test_attributes_and_methods_of_objects(self):
    59        reality = dir(object)
    60        # my_expectation = []
    61        my_expectation = [
    62            '__class__', '__delattr__', '__dir__',
    63            '_[272 chars]k__'
    64        ]
    65        self.assertEqual(reality, my_expectation)
    66
    67
    68# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: Lists differ:
        ['__c[32 chars]', '__doc__', '__eq__',
         '__format__', '__ge__'[231 chars]k__']
     != ['__c[32 chars]', '_[272 chars]k__']
    

    it shows me the entire list below the message

  • I copy (ctrl/command+c) the values from the terminal and paste (ctrl/command+v) them as my_expectation

    58    def test_attributes_and_methods_of_objects(self):
    59        reality = dir(object)
    60        # my_expectation = []
    61        # my_expectation = [
    62        #     '__class__', '__delattr__', '__dir__',
    63        #     '_[272 chars]k__'
    64        # ]
    65        my_expectation = E       - ['__class__',
    66E       -  '__delattr__',
    67E       -  '__dir__',
    68E       -  '__doc__',
    69E       -  '__eq__',
    70E       -  '__format__',
    71E       -  '__ge__',
    72E       -  '__getattribute__',
    73E       -  '__getstate__',
    74E       -  '__gt__',
    75E       -  '__hash__',
    76E       -  '__init__',
    77E       -  '__init_subclass__',
    78E       -  '__le__',
    79E       -  '__lt__',
    80E       -  '__ne__',
    81E       -  '__new__',
    82E       -  '__reduce__',
    83E       -  '__reduce_ex__',
    84E       -  '__repr__',
    85E       -  '__setattr__',
    86E       -  '__sizeof__',
    87E       -  '__str__',
    88E       -  '__subclasshook__']
    89        self.assertEqual(reality, my_expectation)
    90
    91
    92# Exceptions seen
    

    the terminal is my friend, and shows NameError

    NameError: name 'E' is not defined
    
  • I use the find and replace feature of the Integrated Development Environment (IDE) to remove the extra characters, then remove the commented lines

    58    def test_attributes_and_methods_of_objects(self):
    59        reality = dir(object)
    60        my_expectation = [
    61            '__class__',
    62            '__delattr__',
    63            '__dir__',
    64            '__doc__',
    65            '__eq__',
    66            '__format__',
    67            '__ge__',
    68            '__getattribute__',
    69            '__getstate__',
    70            '__gt__',
    71            '__hash__',
    72            '__init__',
    73            '__init_subclass__',
    74            '__le__',
    75            '__lt__',
    76            '__ne__',
    77            '__new__',
    78            '__reduce__',
    79            '__reduce_ex__',
    80            '__repr__',
    81            '__setattr__',
    82            '__sizeof__',
    83            '__str__',
    84            '__subclasshook__'
    85        ]
    86        self.assertEqual(reality, my_expectation)
    87
    88
    89# Exceptions seen
    

    the test passes. All classes automatically get these attributes, they inherit them because all classes inherit from ‘object’.

    The __init__ method is also inherited which means when I defined it in test_classy_person_says_hello I overwrote the inherited one.

  • I add a git commit message in the other terminal

    git commit -am \
    'add test_attributes_and_methods_of_objects'
    

all classes inherit from ‘object’.


test_making_a_class_w_inheritance

I can make classes with inheritance by stating the parent class


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a new test

    83            '__str__',
    84            '__subclasshook__'
    85        ]
    86        self.assertEqual(reality, my_expectation)
    87
    88    def test_making_a_class_w_inheritance(self):
    89        a_class = src.classes.Doe
    90        assert isinstance(a_class, src.person.Person)
    91
    92
    93# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.classes' has no attribute 'Doe'
    

    because there is no definition for Doe in classes.py


GREEN: make it pass


  • I add a class definition to classes.py

     7class WObject(object): pass
     8
     9
    10class Doe(object): pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert False
    

    because

    • doe is just a name for the Doe class

    • Doe and Person are children of object

  • I change the assertion in test_making_a_class_w_inheritance in test_classes.py

    88    def test_making_a_class_w_inheritance(self):
    89        a_class = src.classes.Doe
    90        # assert isinstance(a_class, src.person.Person)
    91        assert not isinstance(a_class, src.person.Person)
    92
    93
    94# Exceptions seen
    

    the test passes. Doe is not an instance of the Person class.


REFACTOR: make it better


  • I add a call to the `assertIsInstance method`_

    88    def test_making_a_class_w_inheritance(self):
    89        a_class = src.classes.Doe
    90        # assert isinstance(a_class, src.person.Person)
    91        assert not isinstance(a_class, src.person.Person)
    92        self.assertIsInstance(a_class, src.person.Person)
    93
    94
    95# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.classes.Doe'> is not
        an instance of <class 'src.person.Person'>
    
  • I change assertIsInstance_ to assertNotIsInstance_

    88    def test_making_a_class_w_inheritance(self):
    89        a_class = src.classes.Doe
    90        # assert isinstance(a_class, src.person.Person)
    91        assert not isinstance(a_class, src.person.Person)
    92        # self.assertIsInstance(a_class, src.person.Person)
    93        self.assertNotIsInstance(
    94            a_class, src.person.Person
    95        )
    96
    97
    98# Exceptions seen
    

    the test passes.

  • I use the `issubclass built-in function`_ to test if Doe is a child of Person

    88    def test_making_a_class_w_inheritance(self):
    89        a_class = src.classes.Doe
    90        # assert isinstance(a_class, src.person.Person)
    91        assert not isinstance(a_class, src.person.Person)
    92        # self.assertIsInstance(a_class, src.person.Person)
    93        self.assertNotIsInstance(
    94            a_class, src.person.Person
    95        )
    96        assert issubclass(a_class, src.person.Person)
    97
    98
    99# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    E       AssertionError: assert
    

    because Doe is not a child of Person, yet.

  • I change the parent of Doe from object to Person in classes.py

    10# class Doe(object): pass
    11class Doe(person.Person): pass
    

    the terminal is my friend, and shows NameError

    NameError: name 'person' is not defined
    

    because there is no definition for person in classes.py

  • I add an import statement at the top of classes.py

    1import person
    2
    3
    4class WPass: pass
    

    the terminal is my friend, and shows ModuleNotFoundError

    E   ModuleNotFoundError: No module named 'person'
    

    because the test cannot find person.py in the main project folder where I run the tests from, so it cannot import the Module

  • I add ModuleNotFoundError to the list of Exceptions seen, in test_classes.py

     99# Exceptions seen
    100# AssertionError
    101# NameError
    102# AttributeError
    103# ModuleNotFoundError
    
  • I change the import statement so the path to person.py from the main project folder is correct, in classes.py

    1# import person
    2import src.person
    3
    4
    5class WPass: pass
    

    the terminal does not feel like my friend, it goes back to NameError

    NameError: name 'person' is not defined
    
  • I add src. to the parent of Doe

    14# class Doe(object): pass
    15# class Doe(person.Person): pass
    16class Doe(src.person.Person): pass
    
    • the test passes because Doe is now a child (subclass) of Person.

    • import src.person brings in an object that represents the person.py module from the src folder so I can use it in classes.py.

    • I have to use src.person.Person in classes.py because I am testing from the root folder of the project.

    • The test needs to know where person.py is in relation to where I ran the tests from.

    • This is a problem because if classes.py is run from inside src the import statement will not be able to find src.person from inside src. Same thing if I run the tests from inside tests. That is a problem for another time.

  • I add the `assertNotIsSubclass method`_ to test_making_a_class_w_inheritance in test_classes.py

     88    def test_making_a_class_w_inheritance(self):
     89        a_class = src.classes.Doe
     90        # assert isinstance(a_class, src.person.Person)
     91        assert not isinstance(a_class, src.person.Person)
     92        # self.assertIsInstance(a_class, src.person.Person)
     93        self.assertNotIsInstance(
     94            a_class, src.person.Person
     95        )
     96        assert issubclass(a_class, src.person.Person)
     97        self.assertNotIsSubclass(
     98            a_class, src.person.Person
     99        )
    100
    101
    102# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.classes.Doe'> is a
        subclass of <class 'src.person.Person'>
    
  • I change assertNotIsSubclass_ to assertIsSubclass_

     88    def test_making_a_class_w_inheritance(self):
     89        a_class = src.classes.Doe
     90        # assert isinstance(a_class, src.person.Person)
     91        assert not isinstance(a_class, src.person.Person)
     92        # self.assertIsInstance(a_class, src.person.Person)
     93        self.assertNotIsInstance(
     94            a_class, src.person.Person
     95        )
     96        assert issubclass(a_class, src.person.Person)
     97        # self.assertNotIsSubclass(
     98        self.assertIsSubclass(
     99            a_class, src.person.Person
    100        )
    101
    102
    103# Exceptions seen
    

    the test passes.

  • I add an assertion to show that a_class which points to Doe is just a name for the class not an instance

     88    def test_making_a_class_w_inheritance(self):
     89        a_class = src.classes.Doe
     90        # assert isinstance(a_class, src.person.Person)
     91        assert not isinstance(a_class, src.person.Person)
     92        # self.assertIsInstance(a_class, src.person.Person)
     93        self.assertNotIsInstance(
     94            a_class, src.person.Person
     95        )
     96        assert issubclass(a_class, src.person.Person)
     97        # self.assertNotIsSubclass(
     98        self.assertIsSubclass(
     99            a_class, src.person.Person
    100        )
    101        assert isinstance(a_class, a_class)
    102
    103
    104# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    E       AssertionError: assert False
    

    because a class is not an instance

  • I change the assertion

     88    def test_making_a_class_w_inheritance(self):
     89        a_class = src.classes.Doe
     90        # assert isinstance(a_class, src.person.Person)
     91        assert not isinstance(a_class, src.person.Person)
     92        # self.assertIsInstance(a_class, src.person.Person)
     93        self.assertNotIsInstance(
     94            a_class, src.person.Person
     95        )
     96        assert issubclass(a_class, src.person.Person)
     97        # self.assertNotIsSubclass(
     98        self.assertIsSubclass(
     99            a_class, src.person.Person
    100        )
    101        # assert isinstance(a_class, a_class)
    102        assert not isinstance(a_class, a_class)
    103
    104
    105# Exceptions seen
    

    the test passes.

  • I add assertIsInstance_

     88    def test_making_a_class_w_inheritance(self):
     89        a_class = src.classes.Doe
     90        # assert isinstance(a_class, src.person.Person)
     91        assert not isinstance(a_class, src.person.Person)
     92        # self.assertIsInstance(a_class, src.person.Person)
     93        self.assertNotIsInstance(
     94            a_class, src.person.Person
     95        )
     96        assert issubclass(a_class, src.person.Person)
     97        # self.assertNotIsSubclass(
     98        self.assertIsSubclass(
     99            a_class, src.person.Person
    100        )
    101        # assert isinstance(a_class, a_class)
    102        assert not isinstance(a_class, a_class)
    103        self.assertIsInstance(a_class, a_class)
    104
    105
    106# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError:
        <class 'src.classes.Doe'> is not
        an instance of <class 'src.classes.Doe'>
    

    because a class is not an instance

  • I change assertIsInstance_ to assertNotIsInstance_

     88    def test_making_a_class_w_inheritance(self):
     89        a_class = src.classes.Doe
     90        # assert isinstance(a_class, src.person.Person)
     91        assert not isinstance(a_class, src.person.Person)
     92        # self.assertIsInstance(a_class, src.person.Person)
     93        self.assertNotIsInstance(
     94            a_class, src.person.Person
     95        )
     96        assert issubclass(a_class, src.person.Person)
     97        # self.assertNotIsSubclass(
     98        self.assertIsSubclass(
     99            a_class, src.person.Person
    100        )
    101        # assert isinstance(a_class, a_class)
    102        assert not isinstance(a_class, a_class)
    103        # self.assertIsInstance(a_class, a_class)
    104        self.assertNotIsInstance(a_class, a_class)
    105
    106
    107# Exceptions seen
    

what is the difference between an instance and a class?


  • I add another assertion, this time with an instance of Doe

     88    def test_making_a_class_w_inheritance(self):
     89        a_class = src.classes.Doe
     90        # assert isinstance(a_class, src.person.Person)
     91        assert not isinstance(a_class, src.person.Person)
     92        # self.assertIsInstance(a_class, src.person.Person)
     93        self.assertNotIsInstance(
     94            a_class, src.person.Person
     95        )
     96        assert issubclass(a_class, src.person.Person)
     97        # self.assertNotIsSubclass(
     98        self.assertIsSubclass(
     99            a_class, src.person.Person
    100        )
    101        # assert isinstance(a_class, a_class)
    102        assert not isinstance(a_class, a_class)
    103        # self.assertIsInstance(a_class, a_class)
    104        self.assertNotIsInstance(a_class, a_class)
    105
    106        an_instance = src.classes.Doe()
    107        assert not isinstance(
    108            an_instance, src.person.Person
    109        )
    110
    111
    112# Exceptions seen
    

    the terminal is my friend, and shows TypeError

    TypeError: Person.__init__() missing 1
               required positional argument: 'first_name'
    

    because the __init__ method of the Person class takes one required argument for first_name and I called Doe to make an instance. How did Person.__init__ get called?


what happens when the child calls the parent?


  • Here is what is happens when an_instance = src.class.Doe() runs

    an_instance = src.classes.Doe()
                  Doe # has no __init__, call Person
                  Person.__init__()
    

    which raises TypeError

  • I add TypeError to the list of Exceptions seen

    112# Exceptions seen
    113# AssertionError
    114# NameError
    115# AttributeError
    116# ModuleNotFoundError
    117# TypeError
    

how to call the parent from the child


  • I add the super built-in function to Doe to call the parent (Person) __init__ method directly, in classes.py

    14# class Doe(object): pass
    15# class Doe(person.Person): pass
    16# class Doe(src.person.Person): pass
    17class Doe(src.person.Person):
    18
    19    def __init__(self):
    20        super().__init__()
    
  • I add a value to src.classes.Doe in test_making_a_class_w_inheritance in test_classes.py

    106        # an_instance = src.classes.Doe()
    107        an_instance = src.classes.Doe('first_name')
    108        assert not isinstance(
    109            an_instance, src.person.Person
    110        )
    111
    112
    113# Exceptions seen
    

    the terminal is my friend, and shows TypeError

    TypeError: Doe.__init__() takes
               1 positional argument but 2 were given
    

    because this happens when an_instance = src.classes.Doe('first_name') runs

    an_instance = src.classes.Doe('first_name')
                  Doe.__init__('first_name')
    

    which raises TypeError because the definition for the __init__ method in Doe only takes one positional argument (self) and it was called with two (self and first_name)

  • I add a parameter for first_name to the __init__ method of Doe in classes.py

    14# class Doe(object): pass
    15# class Doe(person.Person): pass
    16# class Doe(src.person.Person): pass
    17class Doe(src.person.Person):
    18
    19    # def __init__(self):
    20    def __init__(self, first_name):
    21        super().__init__()
    

    the terminal is my friend, and shows TypeError

    TypeError: Person.__init__() missing 1
               required positional argument: 'first_name'
    

    because this happens when an_instance = src.classes.Doe('first_name') runs

    an_instance = src.classes.Doe('first_name')
                  Doe.__init__('first_name')
                      super().__init__()
                  Person.__init__()
    

    which raises TypeError

  • I add the required parameter to super().__init__() in Doe

    14# class Doe(object): pass
    15# class Doe(person.Person): pass
    16# class Doe(src.person.Person): pass
    17class Doe(src.person.Person):
    18
    19    # def __init__(self):
    20    def __init__(self, first_name):
    21        # super().__init__()
    22        super().__init__(first_name)
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert not True
    

    because

    • an instance of Doe is an instance of Person

    • Person is the parent of Doe

    • the test shows that this happens when an_instance = src.classes.Doe('first_name') runs

      an_instance = src.classes.Doe('first_name')
                    Doe.__init__('first_name')
                        super().__init__('first_name')
                    Person.__init__('first_name')
      
  • I change the assertion in test_making_a_class_w_inheritance in test_classes.py

    106        # an_instance = src.classes.Doe()
    107        an_instance = src.classes.Doe('first_name')
    108        # assert not isinstance(
    109        assert isinstance(
    110            an_instance, src.person.Person
    111        )
    112
    113
    114# Exceptions seen
    

    the test passes.

  • I add a call to the `assertNotIsInstance method`_

    106        # an_instance = src.classes.Doe()
    107        an_instance = src.classes.Doe('first_name')
    108        # assert not isinstance(
    109        assert isinstance(
    110            an_instance, src.person.Person
    111        )
    112        self.assertNotIsInstance(
    113            an_instance, src.person.Person
    114        )
    115
    116
    117# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <src.classes.Doe object at 0xffff01a2bc34> is
        an instance of <class 'src.person.Person'>
    

    because an instance of Doe is an instance of Person

  • I change assertNotIsInstance_ to assertIsInstance_

    106        # an_instance = src.classes.Doe()
    107        an_instance = src.classes.Doe('first_name')
    108        # assert not isinstance(
    109        assert isinstance(
    110            an_instance, src.person.Person
    111        )
    112        # self.assertNotIsInstance(
    113        self.assertIsInstance(
    114            an_instance, src.person.Person
    115        )
    116
    117
    118# Exceptions seen
    

    the test passes.

  • I add a test for the attributes and methods of the Doe class

    106        # an_instance = src.classes.Doe()
    107        an_instance = src.classes.Doe('first_name')
    108        # assert not isinstance(
    109        assert isinstance(
    110            an_instance, src.person.Person
    111        )
    112        # self.assertNotIsInstance(
    113        self.assertIsInstance(
    114            an_instance, src.person.Person
    115        )
    116
    117        self.assertEqual(
    118            dir(a_class),
    119            []
    120        )
    121
    122
    123# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: Lists differ:
        ['__class__', '__delattr__', '__dict__',
         '[370 chars]llo']
     != []
    
  • I change the expectation to the attributes and methods of the Person class

    117        self.assertEqual(
    118            dir(a_class),
    119            # []
    120            dir(src.person.Person)
    121        )
    122
    123
    124# Exceptions seen
    

    the test passes because Doe has the same attributes and methods as Person because Doe is a child of Parent

  • I remove the commented lines

     88    def test_making_a_class_w_inheritance(self):
     89        a_class = src.classes.Doe
     90
     91        assert not isinstance(a_class, src.person.Person)
     92        self.assertNotIsInstance(
     93            a_class, src.person.Person
     94        )
     95
     96        assert issubclass(a_class, src.person.Person)
     97        self.assertIsSubclass(
     98            a_class, src.person.Person
     99        )
    100
    101        assert not isinstance(a_class, a_class)
    102        self.assertNotIsInstance(a_class, a_class)
    103
    104        an_instance = src.classes.Doe('first_name')
    105        assert isinstance(
    106            an_instance, src.person.Person
    107        )
    108        self.assertIsInstance(
    109            an_instance, src.person.Person
    110        )
    111
    112        self.assertEqual(
    113            dir(a_class),
    114            dir(src.person.Person)
    115        )
    116
    117
    118# Exceptions seen
    
  • I remove the commented lines from classes.py

     1import src.person
     2
     3
     4class WPass: pass
     5
     6
     7class WParentheses(): pass
     8
     9
    10class WObject(object): pass
    11
    12
    13class Doe(src.person.Person):
    14
    15    def __init__(self, first_name):
    16        super().__init__(first_name)
    
  • I add a git commit message in the other terminal

    git commit -am \
    'add test_making_a_class_w_inheritance'
    

I can make a class with inheritance.


test_classes_w_one_parent


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a new test for Inheritance with an assertion

    112        self.assertEqual(
    113            dir(a_class),
    114            dir(src.person.Person)
    115        )
    116
    117    def test_classes_w_one_parent(self):
    118        doe = src.classes.Doe('doe')
    119        self.assertEqual(doe.last_name, '')
    120
    121
    122# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'doe' != ''
    

GREEN: make it pass


I change the expectation

117    def test_classes_w_one_parent(self):
118        doe = src.classes.Doe('doe')
119        # self.assertEqual(doe.last_name, '')
120        self.assertEqual(doe.last_name, 'doe')
121
122
123# Exceptions seen

the test passes because this happens when doe = src.classes.Doe('doe') runs

doe = src.classes.Doe('doe')
      Doe.__init__('doe')
          super().__init__(first_name)
      Person.__init__('first_name')
          Person.__init__('first_name', last_name='doe')
          self.last_name = 'doe' # use the default value

the value for doe.last_name is doe because a method uses the default value for a parameter when it is called without the parameter.


REFACTOR: make it better


  • I add another assertion

    117    def test_classes_w_one_parent(self):
    118        doe = src.classes.Doe('doe')
    119        # self.assertEqual(doe.last_name, '')
    120        self.assertEqual(doe.last_name, 'doe')
    121
    122        joe = src.classes.Doe('joe')
    123        self.assertEqual(joe.last_name, 'blow')
    124
    125
    126# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'doe' != 'blow'
    
  • I add a new class definition to classes.py

    13class Doe(src.person.Person):
    14
    15    def __init__(self, first_name):
    16        super().__init__(first_name)
    17
    18
    19class Blow(src.person.Person): pass
    

    the terminal still shows AssertionError

  • I change joe to use the new Blow class, in test_classes_w_one_parent in test_classes.py

    122        # joe = src.classes.Doe('joe')
    123        joe = src.classes.Blow('joe')
    124        self.assertEqual(joe.last_name, 'blow')
    125
    126
    127# Exceptions seen
    

    the terminal still shows AssertionError

  • I add a class attribute for last_name in the Blow class in classes.py

    19# class Blow(src.person.Person): pass
    20class Blow(src.person.Person):
    21
    22    last_name = 'blow'
    

    the terminal does not feel like my friend, it still shows AssertionError

  • I add the __init__ method to customize the last name

    19# class Blow(src.person.Person): pass
    20class Blow(src.person.Person):
    21
    22    # last_name = 'blow'
    23
    24    def __init__(self):
    25        self.last_name = 'blow'
    

    the terminal is my friend, and shows TypeError

    TypeError: Blow.__init__() takes
               1 positional argument but 2 were given
    

    because this happens when joe = src.classes.Blow('joe') runs

    joe = src.classes.Blow('joe')
          Blow.__init__('joe')
    

    which raises TypeError since the __init__ method o of Blow only takes one positional argument (self) and it got called with two (self and first_name)

  • I add first_name to the parentheses for the __init__ method

    16# class Blow(src.person.Person): pass
    17class Blow(src.person.Person):
    18
    19    # last_name = 'blow'
    20
    21    # def __init__(self):
    22    def __init__(self, first_name):
    23        self.last_name = 'blow'
    

    the test passes because this happens when joe = src.classes.Blow('joe') runs

    joe = src.classes.Blow('joe')
          Blow.__init__('joe')
              self.last_name = 'joe'
    

    I can define classes that are related and have their own defaults. In this test

    • the Doe class has a default last_name that is the same as the default last name for Person

    • the Blow class has a different default last_name

    • Doe and Blow are children (subclasses) of Person

  • I remove the commented lines

    13class Doe(src.person.Person):
    14
    15    def __init__(self, first_name):
    16        super().__init__(first_name)
    17
    18
    19class Blow(src.person.Person):
    20
    21    def __init__(self, first_name):
    22        self.last_name = 'blow'
    
  • In this case there is a simpler way to make joe and doe. I could directly pass the values to the Person class since all the Blow class does is customize the last_name attribute, there is nothing special about it or the Doe class. I add an assertion to test_classes_w_one_parent in test_classes.py

    122        # joe = src.classes.Doe('joe')
    123        joe = src.classes.Blow('joe')
    124        self.assertEqual(joe.last_name, 'blow')
    125
    126        blow = src.person.Person('joe')
    127        self.assertEqual(blow.last_name, joe.last_name)
    128
    129
    130# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'doe' != 'blow'
    
  • I add last_name='blow' to the call

    126        # blow = src.person.Person('joe')
    127        blow = src.person.Person('joe', last_name='blow')
    128        self.assertEqual(blow.last_name, joe.last_name)
    129
    130
    131# Exceptions seen
    

    the test passes. I can make instances of classes by customizing its attributes without having to make a new class.

  • I add an assertion for jane

    126        # blow = src.person.Person('joe')
    127        blow = src.person.Person('joe', last_name='blow')
    128        self.assertEqual(blow.last_name, joe.last_name)
    129
    130        jane = src.person.Person('jane')
    131        self.assertEqual(jane.last_name, blow.last_name)
    132
    133
    134# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'doe' != 'blow'
    
  • I change the expectation

    130        jane = src.person.Person('jane')
    131        # self.assertEqual(jane.last_name, blow.last_name)
    132        self.assertEqual(jane.last_name, doe.last_name)
    133
    134
    135# Exceptions seen
    

    the test passes.

  • I add an assertion for john

    130        jane = src.person.Person('jane')
    131        # self.assertEqual(jane.last_name, blow.last_name)
    132        self.assertEqual(jane.last_name, doe.last_name)
    133
    134        john = src.classes.Smith('john')
    135        self.assertEqual(john.last_name, 'smith')
    136
    137
    138# Exceptions seen
    

    the terminal shows AttributeError

    AttributeError: module 'src.classes' has no attribute 'Smith'
    
  • I add a class definition for Smith to classes.py

    19class Blow(src.person.Person):
    20
    21    def __init__(self, first_name):
    22        self.last_name = 'blow'
    23
    24
    25class Smith(src.person.Person): pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'doe' != 'smith'
    

    because this happens when john = src.classes.Smith('john') runs

    john = src.classes.Smith('john')
           Smith # Smith has no __init__, call Person
           Person.__init__('john')
               Person.__init__('john', last_name='doe')
               self.last_name = 'doe' # use the default value
    
  • I add the __init__ method in Smith

    25# class Smith(src.person.Person): pass
    26class Smith(src.person.Person):
    27
    28    def __init__(self):
    29        self.last_name = 'smith'
    

    the terminal shows TypeError

    TypeError: Smith.__init__() takes 1
               positional argument but 2 were given
    

    because this happens when john = src.classes.Smith('john') runs

    john = src.classes.Smith('john')
           Smith.__init__('john')
    

    the definition for the __init__ method only allows one input (self) and it got called with two (self and first_name)

  • I add first_name in parentheses

    25# class Smith(src.person.Person): pass
    26class Smith(src.person.Person):
    27
    28    # def __init__(self):
    29    def __init__(self, first_name):
    30        self.last_name = 'smith'
    

    the test passes because this happens when john = src.classes.Smith('john') runs

    john = src.classes.Smith('john')
           Smith.__init__('john')
           self.last_name = 'smith'
    
  • I remove the commented lines

    19class Blow(src.person.Person):
    20
    21    def __init__(self, first_name):
    22        self.last_name = 'blow'
    23
    24
    25class Smith(src.person.Person):
    26
    27    def __init__(self, first_name):
    28        self.last_name = 'smith'
    

    the test passes.

  • I add another assertion to test_classes_w_one_parent in test_classes.py

    134        john = src.classes.Smith('john')
    135        self.assertEqual(john.last_name, 'smith')
    136
    137        smith = src.person.Person('john', 'smith')
    138        self.assertEqual(smith.last_name, doe.last_name)
    139
    140
    141# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'smith' != 'doe'
    
  • I change the expectation

    137        smith = src.person.Person('john', 'smith')
    138        # self.assertEqual(smith.last_name, doe.last_name)
    139        self.assertEqual(smith.last_name, john.last_name)
    140
    141
    142# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    112    def test_classes_w_one_parent(self):
    113        doe = src.classes.Doe('doe')
    114        self.assertEqual(doe.last_name, 'doe')
    115
    116        joe = src.classes.Blow('joe')
    117        self.assertEqual(joe.last_name, 'blow')
    118
    119        blow = src.person.Person('joe', last_name='blow')
    120        self.assertEqual(blow.last_name, joe.last_name)
    121
    122        jane = src.person.Person('jane')
    123        self.assertEqual(jane.last_name, doe.last_name)
    124
    125        john = src.classes.Smith('john')
    126        self.assertEqual(john.last_name, 'smith')
    127
    128        smith = src.person.Person('john', 'smith')
    129        self.assertEqual(smith.last_name, john.last_name)
    130
    131
    132# Exceptions seen
    
    • this happens when an instance of Doe is made

      instance = src.classes.Doe('first_name')
                 Doe.__init__('first_name')
                 super().__init__(first_name)
                 Person.__init__('first_name')
                 Person.__init__('first_name', last_name='doe')
                 self.last_name = 'doe' # use the default value
      
    • this happens when an instance of Smith and Blow are made

      instance = src.classes.ClassName('first_name')
                 ClassName.__init__('first_name')
                 self.last_name = 'last_name'
      
    • this happens when instances of the Person class are made

      instance = src.person.Person(first_name, last_name=last_name)
                 Person.__init__(first_name, last_name=last_name)
                 self.first_name = first_name
                 self.last_name = last_name
      
  • I add a git commit message in the other terminal

    git commit -am 'add test_classes_w_one_parent'
    

I can customize child classes with the __init__ method


test_classes_w_multiple_parents

Can a class have more than one parent?


RED: make it fail


  • I go back to the terminal that is running the tests

  • I add a test with an assertion for jane

    133        smith = src.person.Person('john', 'smith')
    134        self.assertEqual(smith.last_name, john.last_name)
    135
    136    def test_classes_w_multiple_parents(self):
    137        jane = src.classes.Jane()
    138        self.assertEqual(jane.first_name, 'jane')
    139
    140
    141# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.classes' has no attribute 'Jane'
    

GREEN: make it pass


  • I add a class for Jane to classes.py

    25class Smith(src.person.Person):
    26
    27    def __init__(self, first_name):
    28        self.last_name = 'smith'
    29
    30
    31class Jane(src.person.Person): pass
    

    the terminal shows TypeError

    TypeError: Person.__init__() missing 1
               required positional argument: 'first_name'
    

    because this happens when jane = src.classes.Jane() runs

    jane = src.classes.Jane()
           Jane # Jane has no __init__, call Person
           Person.__init__()
    

    which raises TypeError since the __init__ method of Person requires one positional argument (first_name) and it got called with zero

  • I add the __init__ method to the definition of Jane

    31# class Jane(src.person.Person): pass
    32class Jane(src.person.Person):
    33
    34    def __init__(self):
    35        return None
    

    the terminal is my friend, and shows AttributeError

    AttributeError: 'Jane' object has no attribute 'first_name'
    
  • I add a value for first_name to the definition

    31# class Jane(src.person.Person): pass
    32class Jane(src.person.Person):
    33
    34    def __init__(self):
    35        self.first_name = 'jane'
    36        return None
    

the test passes.


REFACTOR: make it better


  • I add an assertion for the last name of jane to test_classes_w_multiple_parents in test_classes.py

    136    def test_classes_w_multiple_parents(self):
    137        jane = src.classes.Jane()
    138        self.assertEqual(jane.first_name, 'jane')
    139        self.assertEqual(jane.last_name, 'doe')
    140
    141
    142# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError:
        'Jane' object has no attribute 'last_name'.
        Did you mean: 'first_name'?
    
  • I add a value for last_name to Jane in classes.py

    31# class Jane(src.person.Person): pass
    32class Jane(src.person.Person):
    33
    34    def __init__(self):
    35        self.first_name = 'jane'
    36        self.last_name = 'doe'
    37        return None
    

    the test passes. This is a repetition because

  • I add an assertion to test_classes_w_multiple_parents to make sure Jane is a Doe, in test_classes.py

    136    def test_classes_w_multiple_parents(self):
    137        jane = src.classes.Jane()
    138        self.assertEqual(jane.first_name, 'jane')
    139        self.assertEqual(jane.last_name, 'doe')
    140        self.assertIsSubclass(
    141            src.classes.Jane, src.classes.Doe
    142        )
    143
    144
    145# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.classes.Jane'> is not
        a subclass of <class 'src.classes.Doe'>
    
  • I change the parent of Jane in classes.py

    31# class Jane(src.person.Person): pass
    32# class Jane(src.person.Person):
    33class Jane(Doe):
    34
    35    def __init__(self):
    36        self.first_name = 'jane'
    37        self.last_name = 'doe'
    38        return None
    

    the test passes

  • I add a call to the super built-in function to use to remove the repetition of last_name

    31# class Jane(src.person.Person): pass
    32# class Jane(src.person.Person):
    33class Jane(Doe):
    34
    35    def __init__(self):
    36        super().__init__()
    37        # self.first_name = 'jane'
    38        # self.last_name = 'doe'
    39        return None
    

    the terminal shows TypeError

    TypeError: Doe.__init__() missing 1
               required positional argument: 'first_name'
    

    because this happens when jane = src.classes.Jane() runs

    jane = src.classes.Jane()
           Jane.__init__()
               super().__init__()
           Doe.__init__()
    

    which raises TypeError since the __init__ method of Doe requires two positional arguments (self and first_name) and it got called with one (self)

  • I add jane as the value for first_name in the call to the parent

    31# class Jane(src.person.Person): pass
    32# class Jane(src.person.Person):
    33class Jane(Doe):
    34
    35    def __init__(self):
    36        # super().__init__()
    37        super().__init__('jane')
    38        # self.first_name = 'jane'
    39        # self.last_name = 'doe'
    40        return None
    

    the test is green again because this happens when jane = src.classes.Jane() runs

    jane = src.classes.Jane()
           Jane.__init__()
               super().__init__('jane')
           Doe.__init__('jane')
               super().__init__(first_name)
           Person.__init__('jane')
               Person.__init__('jane', last_name='doe')
               self.first_name = 'jane'
               self.last_name = 'doe' # use the default value
    
  • I add an assertion for mary, another instance of Jane to test_classes_w_multiple_parents in test_classes.py

    136    def test_classes_w_multiple_parents(self):
    137        jane = src.classes.Jane()
    138        self.assertEqual(jane.first_name, 'jane')
    139        self.assertEqual(jane.last_name, 'doe')
    140        self.assertIsSubclass(
    141            src.classes.Jane, src.classes.Doe
    142        )
    143
    144        mary = src.classes.Jane('mary')
    145        self.assertEqual(mary.first_name, 'mary')
    146
    147
    148# Exceptions seen
    

    the terminal is my friend, and shows TypeError

    TypeError: Jane.__init__() takes 1
               positional argument but 2 were given
    

    because this happens when mary = src.classes.Jane('mary') runs

    mary = src.classes.Jane('mary')
           Jane.__init__('mary')
    

    which raises TypeError since the __init__ method takes one positional argument (self) and it was called with two (self and 'mary')

  • I add first_name to the parentheses for the __init__ method of Jane in classes.py

    31# class Jane(src.person.Person): pass
    32# class Jane(src.person.Person):
    33class Jane(Doe):
    34
    35    # def __init__(self):
    36    def __init__(self, first_name):
    37        # super().__init__()
    38        super().__init__('jane')
    39        # self.first_name = 'jane'
    40        # self.last_name = 'doe'
    41        return None
    

    the terminal is my friend, and shows TypeError

    TypeError: Jane.__init__() missing 1
               required positional argument: 'first_name'
    

    I broke the assertion for jane because this happens when jane = src.classes.Jane() runs

    jane = src.classes.Jane()
           Jane.__init__()
    

    which raises TypeError since the __init__ method takes two required positional arguments (self and first_name) and the call only sends one (self)

  • I add a default value to make first_name optional

    31# class Jane(src.person.Person): pass
    32# class Jane(src.person.Person):
    33class Jane(Doe):
    34
    35    # def __init__(self):
    36    # def __init__(self, first_name):
    37    def __init__(self, first_name='jane'):
    38        # super().__init__()
    39        super().__init__('jane')
    40        # self.first_name = 'jane'
    41        # self.last_name = 'doe'
    42        return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'jane' != 'mary'
    

    because this happens when mary = src.classes.Jane('mary') runs

  • I change the call to the super built-in function to use the name instead of a fixed value

    31# class Jane(src.person.Person): pass
    32# class Jane(src.person.Person):
    33class Jane(Doe):
    34
    35    # def __init__(self):
    36    # def __init__(self, first_name):
    37    def __init__(self, first_name='jane'):
    38        # super().__init__()
    39        # super().__init__('jane')
    40        super().__init__(first_name)
    41        # self.first_name = 'jane'
    42        # self.last_name = 'doe'
    43        return
    

    the test passes.

  • I remove the commented lines and return None

    25class Smith(src.person.Person):
    26
    27    def __init__(self, first_name):
    28        self.last_name = 'smith'
    29
    30
    31class Jane(Doe):
    32
    33    def __init__(self, first_name='jane'):
    34        super().__init__(first_name)
    
  • I add an assertion that will fail, for the last name of mary in test_classes_w_multiple_parents in test_classes.py

    144        mary = src.classes.Jane('mary')
    145        self.assertEqual(mary.first_name, 'mary')
    146        self.assertEqual(mary.last_name, mary.first_name)
    147
    148
    149# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'doe' != 'mary'
    
  • I change the expectation to match reality

    144        mary = src.classes.Jane('mary')
    145        self.assertEqual(mary.first_name, 'mary')
    146        # self.assertEqual(mary.last_name, mary.first_name)
    147        self.assertEqual(mary.last_name, jane.last_name)
    148
    149
    150# Exceptions seen
    

    the test passes because mary and jane are instances of Jane


  • I add an assertion for joe

    136    def test_classes_w_multiple_parents(self):
    137        joe = src.classes.Joe()
    138        self.assertEqual(joe.first_name, 'joe')
    139
    140        jane = src.classes.Jane()
    141        self.assertEqual(jane.first_name, 'jane')
    142        self.assertEqual(jane.last_name, 'doe')
    143        self.assertIsSubclass(
    144            src.classes.Jane, src.classes.Doe
    145        )
    146
    147        mary = src.classes.Jane('mary')
    148        self.assertEqual(mary.first_name, 'mary')
    149        # self.assertEqual(mary.last_name, mary.first_name)
    150        self.assertEqual(mary.last_name, jane.last_name)
    151
    152
    153# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError:
        module 'src.classes' has no attribute 'Joe'.
        Did you mean: 'Doe'?
    
  • I add a definition for the Joe class to classes.py

    31class Jane(Doe):
    32
    33    def __init__(self, first_name='jane'):
    34        super().__init__(first_name)
    35
    36
    37class Joe(src.person.Person): pass
    

    the terminal is my friend, and shows TypeError

    TypeError: Person.__init__() missing 1
               required positional argument: 'first_name'
    

    because this happens when joe = src.classes.Joe() runs

    joe = src.classes.Joe()
          Person.__init__()
    

    which raises TypeError since the __init__ method takes two required positional argument (self and first_name) and it was called with one (self)

  • I add the __init__ method to Joe

    37# class Joe(src.person.Person): pass
    38class Joe(src.person.Person):
    39
    40    def __init__(self):
    41        return None
    

    the terminal is my friend, and shows AttributeError

    AttributeError: 'Joe' object has no attribute 'first_name'
    
  • I add self.first_name to Joe with a value

    37# class Joe(src.person.Person): pass
    38class Joe(src.person.Person):
    39
    40    def __init__(self):
    41        self.first_name = 'joe'
    42        return None
    

    the test passes.

  • I add an assertion to test_classes_w_multiple_parents to make sure that joe is a Blow, in test_classes.py

    131    def test_classes_w_multiple_parents(self):
    132        joe = src.classes.Joe()
    133        # self.assertEqual(joe.first_name, 'mary')
    134        self.assertEqual(joe.first_name, 'joe')
    135        self.assertEqual(joe.last_name, 'blow')
    

    the terminal is my friend, and shows AttributeError

    AttributeError:
        'Joe' object has no attribute 'last_name'.
        Did you mean: 'first_name'?
    
  • I add last_name to the __init__ method of Joe in classes.py

    37# class Joe(src.person.Person): pass
    38class Joe(src.person.Person):
    39
    40    def __init__(self):
    41        self.first_name = 'joe'
    42        self.last_name = 'blow'
    43        return None
    

    the test passes. I cheated, which means I need a better test.

  • I add issubclass_ to test_classes_w_multiple_parents to make sure Joe is a child (subclass) of Blow, in test_classes.py

    131    def test_classes_w_multiple_parents(self):
    132        joe = src.classes.Joe()
    133        self.assertEqual(joe.first_name, 'joe')
    134        self.assertEqual(joe.last_name, 'blow')
    135        assert issubclass(
    136            src.classes.Joe, src.classes.Blow
    137        )
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert False
    
  • I change the parent of Joe to Blow in classes.py

    37# class Joe(src.person.Person): pass
    38# class Joe(src.person.Person):
    39class Joe(Blow):
    40
    41    def __init__(self):
    42        self.first_name = 'joe'
    43        self.last_name = 'blow'
    44        return None
    

    the test passes.

  • I no longer need self.last_name = 'blow' because it is a repetition. I add a call to the super built-in function

    37# class Joe(src.person.Person): pass
    38# class Joe(src.person.Person):
    39class Joe(Blow):
    40
    41    def __init__(self):
    42        super().__init__('joe')
    43        # self.first_name = 'joe'
    44        # self.last_name = 'blow'
    45        return None
    

    the terminal is my friend, and shows AttributeError

    AttributeError:
        'Joe' object has no attribute 'first_name'.
        Did you mean: 'last_name'?
    

    because this happens when joe = src.classes.Joe() runs

    joe = src.classes.Joe()
          Joe.__init__()
              super().__init__('joe')
          Blow.__init__('joe')
              self.last_name = 'blow'
    

    there is no assignment of a value to the first_name attribute in Blow

  • I add self.first_name to Blow

    19class Blow(src.person.Person):
    20
    21    def __init__(self, first_name):
    22        self.first_name = first_name
    23        self.last_name = 'blow'
    

    the test passes because this happens when joe = src.classes.Joe() runs

    joe = src.classes.Joe()
          Joe.__init__()
              super().__init__('joe')
          Blow.__init__('joe')
              self.first_name = 'joe'
              self.last_name = 'blow'
    
  • I remove the commented lines and return None from Joe

    32class Jane(Doe):
    33
    34    def __init__(self, first_name='jane'):
    35        super().__init__(first_name)
    36
    37
    38class Joe(Blow):
    39
    40    def __init__(self):
    41        super().__init__('joe')
    
  • I add a call to the `assertNotIsSubclass method`_ in test_classes_w_multiple_parents in test_classes.py

    131    def test_classes_w_multiple_parents(self):
    132        joe = src.classes.Joe()
    133        self.assertEqual(joe.first_name, 'joe')
    134        self.assertEqual(joe.last_name, 'blow')
    135        assert issubclass(
    136            src.classes.Joe, src.classes.Blow
    137        )
    138        self.assertNotIsSubclass(
    139            src.classes.Joe, src.classes.Blow
    140        )
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <src.classes.Joe object at 0xffffabcdef80>
        is a subclass of <class 'src.classes.Blow'>
    
  • I change assertNotIsSubclass_ to assertIsSubclass_ to make the statement True

    136    def test_classes_w_multiple_parents(self):
    137        joe = src.classes.Joe()
    138        self.assertEqual(joe.first_name, 'joe')
    139        self.assertEqual(joe.last_name, 'blow')
    140        assert issubclass(
    141            src.classes.Joe, src.classes.Blow
    142        )
    143        # self.assertNotIsSubclass(
    144        self.assertIsSubclass(
    145            src.classes.Joe, src.classes.Blow
    146        )
    

    the test passes.

  • I change mary to be an instance of Mary, a child (subclass) of Jane

    155        # mary = src.classes.Jane('mary')
    156        mary = src.classes.Mary()
    157        self.assertEqual(mary.first_name, 'mary')
    158        # self.assertEqual(mary.last_name, mary.first_name)
    159        self.assertEqual(mary.last_name, jane.last_name)
    160
    161
    162# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.classes' has no attribute 'Mary'
    
  • I add a class definition for Mary to classes.py

    38class Joe(Blow):
    39
    40    def __init__(self):
    41        super().__init__('joe')
    42
    43
    44class Mary(Jane): pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'jane' != 'mary'
    

    because this happens when mary = src.classes.Mary() runs

    mary = src.classes.Mary()
           Mary # has no __init__ call Jane
           Jane.__init__()
               # use the default value
               Jane.__init__(first_name='jane')
               super().__init__(first_name)
           Doe.__init__('jane')
               super().__init__(first_name)
           Person.__init__('jane')
               Person.__init__('jane', last_name='doe')
               self.first_name = 'jane'
               self.last_name = 'doe' # use the default value
    
  • I add the __init__ method to Mary

    44# class Mary(Jane): pass
    45class Mary(Jane):
    46
    47    def __init__(self):
    48        self.first_name = 'mary'
    

    the terminal shows AttributeError

    AttributeError:
        'Mary' object has no attribute 'last_name'.
        Did you mean: 'first_name'?
    

    because this happens when mary = src.classes.Mary() runs

    mary = src.classes.Mary()
           Mary.__init__()
               self.first_name = 'mary'
    
  • I add a value for last_name

    44# class Mary(Jane): pass
    45class Mary(Jane):
    46
    47    def __init__(self):
    48        self.first_name = 'mary'
    49        self.last_name = 'doe'
    

    the test passes. This is a repetition because

    • Mary is a Jane

    • Jane is a Doe

    • Doe is a Person

    • the default value for last_name in Person is 'doe'

  • I add a call to the super built-in function to remove the repetition

    44# class Mary(Jane): pass
    45class Mary(Jane):
    46
    47    def __init__(self):
    48        super().__init__('mary')
    49        # self.first_name = 'mary'
    50        # self.last_name = 'doe'
    

    the test is still green because this happens when mary = src.classes.Mary() runs

    mary = src.classes.Mary()
           Mary.__init__()
               super().__init__('mary')
           Jane.__init__('mary')
                super().__init__(first_name)
           Doe.__init__('mary')
                super().__init__(first_name)
           Person.__init__('mary')
                Person.__init__('mary', last_name='doe')
                self.first_name = 'mary'
                self.last_name = 'doe' # use the default value
    
  • I add an assertion with the `issubclass built-in function`_ to test_classes_w_multiple_parents in test_classes.py

    155        # mary = src.classes.Jane('mary')
    156        mary = src.classes.Mary()
    157        self.assertEqual(mary.first_name, 'mary')
    158        # self.assertEqual(mary.last_name, mary.first_name)
    159        self.assertEqual(mary.last_name, jane.last_name)
    160        assert not issubclass(
    161            src.classes.Mary, src.classes.Jane
    162        )
    163
    164
    165# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert not True
    
  • I change the assertion to make it True

    155        # mary = src.classes.Jane('mary')
    156        mary = src.classes.Mary()
    157        self.assertEqual(mary.first_name, 'mary')
    158        # self.assertEqual(mary.last_name, mary.first_name)
    159        self.assertEqual(mary.last_name, jane.last_name)
    160        # assert not issubclass(
    161        assert issubclass(
    162            src.classes.Mary, src.classes.Jane
    163        )
    164
    165
    166# Exceptions seen
    

    the test passes.

  • I add a call to assertNotIsSubclass_

    155        # mary = src.classes.Jane('mary')
    156        mary = src.classes.Mary()
    157        self.assertEqual(mary.first_name, 'mary')
    158        # self.assertEqual(mary.last_name, mary.first_name)
    159        self.assertEqual(mary.last_name, jane.last_name)
    160        # assert not issubclass(
    161        assert issubclass(
    162            src.classes.Mary, src.classes.Jane
    163        )
    164        self.assertNotIsSubclass(
    165            src.classes.Mary, src.classes.Jane
    166        )
    167
    168
    169# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.classes.Mary'> is
        a subclass of <class 'src.classes.Jane'>
    
  • I change assertNotIsSubclass_ to the `assertIsSubclass method`_

    155        # mary = src.classes.Jane('mary')
    156        mary = src.classes.Mary()
    157        self.assertEqual(mary.first_name, 'mary')
    158        # self.assertEqual(mary.last_name, mary.first_name)
    159        self.assertEqual(mary.last_name, jane.last_name)
    160        # assert not issubclass(
    161        assert issubclass(
    162            src.classes.Mary, src.classes.Jane
    163        )
    164        # self.assertNotIsSubclass(
    165        self.assertIsSubclass(
    166            src.classes.Mary, src.classes.Jane
    167        )
    168
    169
    170# Exceptions seen
    

    the test passes.


what happens when the child has more than one parent?


  • I add an assertion to test if I can make Joe and Jane both be parents of Mary?

    155        # mary = src.classes.Jane('mary')
    156        mary = src.classes.Mary()
    157        self.assertEqual(mary.first_name, 'mary')
    158        # self.assertEqual(mary.last_name, mary.first_name)
    159        self.assertEqual(mary.last_name, jane.last_name)
    160        # assert not issubclass(
    161        assert issubclass(
    162            src.classes.Mary, src.classes.Jane
    163        )
    164        # self.assertNotIsSubclass(
    165        self.assertIsSubclass(
    166            src.classes.Mary, src.classes.Jane
    167        )
    168        assert issubclass(
    169            src.classes.Mary, src.classes.Joe
    170        )
    171
    172
    173# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert False
    

    because Mary is not a child (subclass) of Joe

  • I add Joe as a parent of Mary in classes.py

    44# class Mary(Jane): pass
    45# class Mary(Jane):
    46class Mary(Jane, Joe):
    47
    48    def __init__(self):
    49        super().__init__('mary')
    50        # self.first_name = 'mary'
    51        # self.last_name = 'doe'
    

    the terminal is my friend, and shows TypeError

    TypeError: Joe.__init__() takes 1
               positional argument but 2 were
    

    because this happens when mary = src.classes.Mary() runs

    mary = src.classes.Mary()
           Mary.__init__()
               super().__init__('mary')
           Jane.__init__('mary')
               super().__init__(first_name)
           Joe.__init__('mary')
    

    which raises TypeError since the __init__ method of Joe only takes one positional argument (self) and it got called with two (self and mary)

  • I change the __init__ method in Joe to take a first_name argument

    38class Joe(Blow):
    39
    40    # def __init__(self):
    41    def __init__(self, first_name):
    42        super().__init__('joe')
    

    the terminal is my friend, and shows TypeError

    TypeError: Joe.__init__() missing 1
               required positional argument: 'first_name'
    

    I broke the call that makes joe = src.classes.Joe() because the __init__ method now has two required positional arguments (self and first_name) and it was called with one (self)

  • I add a default value to make first_name optional

    44class Joe(Blow):
    45
    46    # def __init__(self):
    47    # def __init__(self, first_name):
    48    def __init__(self, first_name='joe'):
    49        super().__init__('joe')
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'joe' != 'mary'
    

    for the first name of mary because this happens when mary = src.classes.Mary() runs

    mary = src.classes.Mary()
           Mary.__init__()
               super().__init__('mary')
           Jane.__init__('mary')
               super().__init__(first_name)
           Joe.__init__('mary')
               super().__init__('joe') # the problem
           Blow.__init__('joe')
               self.first_name = 'joe'
               self.last_name = 'blow'
    
  • I use the parameter name in the call to super instead of a fixed value in Joe

    38    class Joe(Blow):
    39
    40        # def __init__(self):
    41        # def __init__(self, first_name):
    42        def __init__(self, first_name='joe'):
    43            # super().__init__('joe')
    44            super().__init__(first_name)
    

    the terminal shows AssertionError

    AssertionError: 'blow' != 'doe'
    
  • I change the expectation of the assertion in test_classes_w_multiple_parents for the last name of mary in test_classes.py

    148        # mary = src.classes.Jane('mary')
    149        mary = src.classes.Mary()
    150        self.assertEqual(mary.first_name, 'mary')
    151        # self.assertEqual(mary.last_name, mary.first_name)
    152        # self.assertEqual(mary.last_name, jane.last_name)
    153        self.assertEqual(mary.last_name, joe.last_name)
    154        # assert not issubclass(
    155        assert issubclass(
    156            src.classes.Mary, src.classes.Jane
    157        )
    158        # self.assertNotIsSubclass(
    159        self.assertIsSubclass(
    160            src.classes.Mary, src.classes.Jane
    161        )
    162        assert issubclass(
    163            src.classes.Mary, src.classes.Joe
    164        )
    165
    166
    167# Exceptions seen
    

    the test passes.

  • I add a call to the `assertNotIsSubclass method`_

    148        # mary = src.classes.Jane('mary')
    149        mary = src.classes.Mary()
    150        self.assertEqual(mary.first_name, 'mary')
    151        # self.assertEqual(mary.last_name, mary.first_name)
    152        # self.assertEqual(mary.last_name, jane.last_name)
    153        self.assertEqual(mary.last_name, joe.last_name)
    154        self.assertEqual(mary.eye_color, jane.eye_color)
    155        # assert not issubclass(
    156        assert issubclass(
    157            src.classes.Mary, src.classes.Jane
    158        )
    159        # self.assertNotIsSubclass(
    160        self.assertIsSubclass(
    161            src.classes.Mary, src.classes.Jane
    162        )
    163        assert issubclass(
    164            src.classes.Mary, src.classes.Joe
    165        )
    166        self.assertNotIsSubclass(
    167            src.classes.Mary, src.classes.Joe
    168        )
    169
    170
    171# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.classes.Mary'> is
        a subclass of <class 'src.classes.Joe'>
    
  • I change assertNotIsSubclass_ to assertIsSubclass_

    148        # mary = src.classes.Jane('mary')
    149        mary = src.classes.Mary()
    150        self.assertEqual(mary.first_name, 'mary')
    151        # self.assertEqual(mary.last_name, mary.first_name)
    152        # self.assertEqual(mary.last_name, jane.last_name)
    153        self.assertEqual(mary.last_name, joe.last_name)
    154        self.assertEqual(mary.eye_color, jane.eye_color)
    155        # assert not issubclass(
    156        assert issubclass(
    157            src.classes.Mary, src.classes.Jane
    158        )
    159        # self.assertNotIsSubclass(
    160        self.assertIsSubclass(
    161            src.classes.Mary, src.classes.Jane
    162        )
    163        assert issubclass(
    164            src.classes.Mary, src.classes.Joe
    165        )
    166        # self.assertNotIsSubclass(
    167        self.assertIsSubclass(
    168            src.classes.Mary, src.classes.Joe
    169        )
    170
    171# Exceptions seen
    

    the test passes.


  • I change the order of the parents of Mary to see what it does to the value of last_name in classes.py

    47# class Mary(Jane): pass
    48# class Mary(Jane):
    49# class Mary(Jane, Joe):
    50class Mary(Joe, Jane):
    51
    52    def __init__(self):
    53        super().__init__('mary')
    54        # self.first_name = 'mary'
    55        # self.last_name = 'doe'
    

    the test is still green because this happens when mary = src.classes.Mary() runs

    mary = src.classes.Mary()
           Mary.__init__()
               super().__init__('mary')
           Joe.__init__('mary')
               super().__init__(first_name)
           Blow.__init__('joe')
               self.first_name = 'joe'
               self.last_name = 'blow'
    

    the __init__ method of Jane never gets called even though it is a parent of Mary

  • I add an assertion to test_classes_w_multiple_parents in classes.py

    148        jane = src.classes.Jane()
    149        self.assertEqual(jane.first_name, 'jane')
    150        self.assertEqual(jane.last_name, 'doe')
    151        self.assertEqual(jane.eye_color, 'brown')
    152        self.assertIsSubclass(
    153            src.classes.Jane, src.classes.Doe
    154        )
    

    the terminal is my friend, and shows AssertionError

    AttributeError: 'Jane' object has no attribute 'eye_color'
    
  • I add a class attribute to Jane in classes.py

    32class Jane(Doe):
    33
    34    def __init__(self, first_name='jane'):
    35        super().__init__(first_name)
    36        self.eye_color = 'brown'
    

    the test passes.

  • I add an assertion for the eye_color attribute of marry in test_classes_w_multiple_parents in test_classes.py

    156        # mary = src.classes.Jane('mary')
    157        mary = src.classes.Mary()
    158        self.assertEqual(mary.first_name, 'mary')
    159        # self.assertEqual(mary.last_name, mary.first_name)
    160        # self.assertEqual(mary.last_name, jane.last_name)
    161        self.assertEqual(mary.last_name, joe.last_name)
    162        self.assertEqual(mary.eye_color, jane.eye_color)
    163        # assert not issubclass(
    164        assert issubclass(
    165            src.classes.Mary, src.classes.Jane
    166        )
    167        # self.assertNotIsSubclass(
    168        self.assertIsSubclass(
    169            src.classes.Mary, src.classes.Jane
    170        )
    171        assert issubclass(
    172            src.classes.Mary, src.classes.Joe
    173        )
    174        # self.assertNotIsSubclass(
    175        self.assertIsSubclass(
    176            src.classes.Mary, src.classes.Joe
    177        )
    178
    179# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: 'Mary' object has no attribute 'eye_color'
    
  • I change the order of the parents of Mary from (Joe, Jane) to (Jane, Joe) in classes.py

    48# class Mary(Jane): pass
    49# class Mary(Jane):
    50class Mary(Jane, Joe):
    51# class Mary(Joe, Jane):
    52
    53    def __init__(self):
    54        super().__init__('mary')
    55        # self.first_name = 'mary'
    56        # self.last_name = 'doe'
    

    the test passes because this happens when mary = src.classes.Mary() runs

    mary = src.classes.Mary()
           Mary.__init__()
               super().__init__('mary')
           Jane.__init__('mary')
               super().__init__(first_name)
               self.eye_color = 'brown'
           Joe.__init__('mary')
               super().__init__('joe') # the problem
           Blow.__init__('joe')
               self.first_name = 'joe'
               self.last_name = 'blow'
    

    the order of the parents matters.


  • I add john to test_classes_w_multiple_parents in test_classes.py

    156        # mary = src.classes.Jane('mary')
    157        mary = src.classes.Mary()
    158        self.assertEqual(mary.first_name, 'mary')
    159        # self.assertEqual(mary.last_name, mary.first_name)
    160        # self.assertEqual(mary.last_name, jane.last_name)
    161        self.assertEqual(mary.last_name, joe.last_name)
    162        self.assertEqual(mary.eye_color, jane.eye_color)
    163        # assert not issubclass(
    164        assert issubclass(
    165            src.classes.Mary, src.classes.Jane
    166        )
    167        # self.assertNotIsSubclass(
    168        self.assertIsSubclass(
    169            src.classes.Mary, src.classes.Jane
    170        )
    171        assert issubclass(
    172            src.classes.Mary, src.classes.Joe
    173        )
    174        # self.assertNotIsSubclass(
    175        self.assertIsSubclass(
    176            src.classes.Mary, src.classes.Joe
    177        )
    178
    179        john = src.classes.John()
    180        self.assertEqual(john.first_name, 'john')
    181
    182
    183# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.classes' has no attribute 'John'
    
  • I add a class definition for John to classes.py

    48# class Mary(Jane): pass
    49# class Mary(Jane):
    50class Mary(Jane, Joe):
    51# class Mary(Joe, Jane):
    52
    53    def __init__(self):
    54        super().__init__('mary')
    55        # self.first_name = 'mary'
    56        # self.last_name = 'doe'
    57
    58
    59class John(src.person.Person): pass
    

    the terminal is my friend, and shows TypeError

    TypeError: Person.__init__() missing 1
               required positional argument: 'first_name'
    

    because this happens when john = src.classes.John() runs

    john = src.classes.John()
           Person.__init__()
    

    which raises TypeError since the __init__ method of Person takes two positional arguments (self and first_name) and it got called with one (self)

  • I add the __init__ method to John in classes.py

    59# class John(src.person.Person): pass
    60class John(src.person.Person):
    61
    62    def __init__(self):
    63        self.first_name = 'john'
    

    the test passes.

  • I add an assertion to make sure that John is a child (subclass) of Smith

    179        john = src.classes.John()
    180        self.assertEqual(john.first_name, 'john')
    181        assert issubclass(
    182            src.classes.John, src.classes.Smith
    183        )
    184
    185
    186# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert False
    

    no cheating this time.

  • I change the parent of John to Smith in classes.py

    59# class John(src.person.Person): pass
    60# class John(src.person.Person):
    61class John(Smith):
    62
    63    def __init__(self):
    64        self.first_name = 'john'
    

    the test passes.

  • I add a call to `assertNotIsSubclass`_ in test_classes_w_multiple_parents in test_classes.py

    179        john = src.classes.John()
    180        self.assertEqual(john.first_name, 'john')
    181        assert issubclass(
    182            src.classes.John, src.classes.Smith
    183        )
    184        self.assertNotIsSubclass(
    185            src.classes.John, src.classes.Smith
    186        )
    187
    188
    189# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.classes.John'> is
        a subclass of <class 'src.classes.Smith'>
    
  • I change the call to `assertNotIsSubclass`_ to the `assertIsSubclass method`_

    179        john = src.classes.John()
    180        self.assertEqual(john.first_name, 'john')
    181        assert issubclass(
    182            src.classes.John, src.classes.Smith
    183        )
    184        # self.assertNotIsSubclass(
    185        self.assertIsSubclass(
    186            src.classes.John, src.classes.Smith
    187        )
    188
    189
    190# Exceptions seen
    

    the test passes.

  • I add an assertion for the last_name attribute

    179        john = src.classes.John()
    180        self.assertEqual(john.first_name, 'john')
    181        self.assertEqual(john.last_name, 'smith')
    182        assert issubclass(
    183            src.classes.John, src.classes.Smith
    184        )
    185        # self.assertNotIsSubclass(
    186        self.assertIsSubclass(
    187            src.classes.John, src.classes.Smith
    188        )
    189
    190
    191# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError:
        'John' object has no attribute 'last_name'.
        Did you mean: 'first_name'?
    
  • I add a class attribute for last_name in John in classes.py

    59# class John(src.person.Person): pass
    60# class John(src.person.Person):
    61class John(Smith):
    62
    63    def __init__(self):
    64        self.first_name = 'john'
    65        self.last_name = 'smith'
    

    the test passes. This is a repetition because

  • I add a call to the super built-in function so instances of John can inherit the last_name class attribute

    59# class John(src.person.Person): pass
    60# class John(src.person.Person):
    61class John(Smith):
    62
    63    def __init__(self):
    64        super().__init__('john')
    65        # self.first_name = 'john'
    66        # self.last_name = 'smith'
    

    the terminal is my friend, and shows AttributeError

    AttributeError:
        'John' object has no attribute 'first_name'.
        Did you mean: 'last_name'?
    
  • I add the attribute to Smith

    26class Smith(src.person.Person):
    27
    28    def __init__(self, first_name):
    29        self.first_name = first_name
    30        self.last_name = 'smith'
    

    the test passes because this happens when john = src.classes.John() runs

    john = src.classes.John()
           John.__init__()
               super().__init__('john')
           Smith.__init__('john')
               self.first_name = 'john'
               self.last_name = 'smith'
    

  • I add another person, a child (subclass) of John to test_classes_w_multiple_parents in test_classes.py

    179        john = src.classes.John()
    180        self.assertEqual(john.first_name, 'john')
    181        self.assertEqual(john.last_name, 'smith')
    182        assert issubclass(
    183            src.classes.John, src.classes.Smith
    184        )
    185        # self.assertNotIsSubclass(
    186        self.assertIsSubclass(
    187            src.classes.John, src.classes.Smith
    188        )
    189
    190        lil = src.classes.Lil()
    191        assert issubclass(
    192            src.classes.Lil, src.classes.John
    193        )
    194
    195
    196# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.classes' has no attribute 'Lil'
    
  • I add a class definition for Lil to classes.py

    60# class John(src.person.Person): pass
    61# class John(src.person.Person):
    62class John(Smith):
    63
    64    def __init__(self):
    65        super().__init__('john')
    66        # self.first_name = 'john'
    67        # self.last_name = 'smith'
    68
    69
    70class Lil(John): pass
    

    the test passes.

  • I add a call to the `assertNotIsSubclass method`_ in test_classes_w_multiple_parents in test_classes.py

    190        lil = src.classes.Lil()
    191        assert issubclass(
    192            src.classes.Lil, src.classes.John
    193        )
    194        self.assertNotIsSubclass(
    195            src.classes.Lil, src.classes.John
    196        )
    197
    198
    199# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.classes.Lil'> is
        a subclass of <class 'src.classes.John'>
    
  • I change assertNotIsSubclass_ to assertIsSubclass_

    190        lil = src.classes.Lil()
    191        assert issubclass(
    192            src.classes.Lil, src.classes.John
    193        )
    194        # self.assertNotIsSubclass(
    195        self.assertIsSubclass(
    196            src.classes.Lil, src.classes.John
    197        )
    198
    199
    200# Exceptions seen
    

    the test passes

  • I add an assertion to test John and Mary as parents of Lil?

    190        lil = src.classes.Lil()
    191        assert issubclass(
    192            src.classes.Lil, src.classes.John
    193        )
    194        # self.assertNotIsSubclass(
    195        self.assertIsSubclass(
    196            src.classes.Lil, src.classes.John
    197        )
    198        assert issubclass(
    199            src.classes.Lil, src.classes.Mary
    200        )
    201
    202
    203# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: assert False
    
  • I add Mary as a parent to Lil in classes.py

    70# class Lil(John): pass
    71class Lil(John, Mary): pass
    

    the test passes.

  • I add an assertion for the first_name attribute of lil in test_classes.py

    190        lil = src.classes.Lil()
    191        self.assertEqual(lil.first_name, 'lil')
    192        assert issubclass(
    193            src.classes.Lil, src.classes.John
    194        )
    195        # self.assertNotIsSubclass(
    196        self.assertIsSubclass(
    197            src.classes.Lil, src.classes.John
    198        )
    199        assert issubclass(
    200            src.classes.Lil, src.classes.Mary
    201        )
    202
    203
    204# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: 'john' != 'lil'
    

    because this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil # has no __init__, call John
          John.__init__()
               super().__init__('john')
          Smith.__init__('john')
              self.first_name = 'john'
              self.last_name = 'smith'
    
  • I add the __init__ method with a value for the first_name attribute in Lil in classes.py

    70# class Lil(John): pass
    71# class Lil(John, Mary): pass
    72class Lil(John, Mary):
    73
    74    def __init__(self):
    75        self.first_name = 'lil'
    

    the test passes.

  • I add an assertion for the last_name attribute of lil, in test_classes_w_multiple_parents in test_classes.py

    190        lil = src.classes.Lil()
    191        self.assertEqual(lil.first_name, 'lil')
    192        self.assertEqual(lil.last_name, mary.last_name)
    193        assert issubclass(
    194            src.classes.Lil, src.classes.John
    195        )
    196        # self.assertNotIsSubclass(
    197        self.assertIsSubclass(
    198            src.classes.Lil, src.classes.John
    199        )
    200        assert issubclass(
    201            src.classes.Lil, src.classes.Mary
    202        )
    203
    204
    205# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError:
        'Lil' object has no attribute 'last_name'.
        Did you mean: 'first_name'?
    
  • I add a value for last_name to Lil in classes.py

    70# class Lil(John): pass
    71# class Lil(John, Mary): pass
    72class Lil(John, Mary):
    73
    74    def __init__(self):
    75        self.first_name = 'lil'
    76        self.last_name = 'blow'
    

    the test passes. This is a repetition, and a problem if the value of the last_name attribute of the parent changes.

  • I add a call to the super built-in function to remove the repetition

    70# class Lil(John): pass
    71# class Lil(John, Mary): pass
    72class Lil(John, Mary):
    73
    74    def __init__(self):
    75        super().__init__('lil')
    76        # self.first_name = 'lil'
    77        # self.last_name = 'blow'
    

    the terminal shows TypeError

    TypeError: John.__init__() takes 1
               positional argument but 2 were given
    

    because this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil.__init__()
              super().__init__('lil')
          John.__init__()
    

    which raises TypeError since the __init__ method of John takes one positional argument (self) and it was called with two (self and lil)

  • I change the __init__ method in John to take in a parameter for first_name with a default value to make it optional

    60# class John(src.person.Person): pass
    61# class John(src.person.Person):
    62class John(Smith):
    63
    64    # def __init__(self):
    65    def __init__(self, first_name='john'):
    66        # super().__init__('john')
    67        super().__init__(first_name)
    68        # self.first_name = 'john'
    69        # self.last_name = 'smith'
    

    the terminal shows AssertionError

    because this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil.__init__()
              super().__init__('lil')
          John.__init__('lil')
              super().__init__(first_name)
          Smith.__init__('lil')
              super().__init__(first_name)
              self.first_name = first_name
              self.last_name = last_name
    

    no other __init__ methods get called for the parents of Lil

  • I add a call to the super built-in function in Smith to see what will happen

    26class Smith(src.person.Person):
    27
    28    def __init__(self, first_name):
    29        super().__init__(
    30            first_name=first_name,
    31            last_name='smith',
    32        )
    33        # self.first_name = first_name
    34        # self.last_name = 'smith'
    

    the terminal is my friend, and shows TypeError

    TypeError: Mary.__init__() got
               an unexpected keyword argument 'first_name'
    

    because this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil.__init__()
              super().__init__('lil')
          John.__init__('lil')
              super().__init__(first_name)
          Smith.__init__('lil')
              super().__init__(
                  first_name=first_name, last_name='smith',
              )
          Mary.__init__('lil', 'smith')
    

    which raises TypeError since the __init__ method of Mary was called with a keyword argument not self

  • I add first_name with a default value to the __init__ method of Mary

    50# class Mary(Jane): pass
    51# class Mary(Jane):
    52class Mary(Jane, Joe):
    53# class Mary(Joe, Jane):
    54
    55    # def __init__(self):
    56    def __init__(self, first_name='mary'):
    57        # super().__init__('mary')
    58        super().__init__(first_name)
    59        # self.first_name = 'mary'
    60        # self.last_name = 'doe'
    

    the terminal is my friend, and shows TypeError

    TypeError: Mary.__init__() got
               an unexpected keyword argument 'last_name'.
               Did you mean 'first_name'?
    
  • I add last_name to the __init__ method

    50# class Mary(Jane): pass
    51# class Mary(Jane):
    52class Mary(Jane, Joe):
    53# class Mary(Joe, Jane):
    54
    55    # def __init__(self):
    56    # def __init__(self, first_name='mary'):
    57    def __init__(self, first_name='mary', last_name):
    58        # super().__init__('mary')
    59        super().__init__(first_name)
    60        # self.first_name = 'mary'
    61        # self.last_name = 'doe'
    

    the terminal shows SyntaxError

    SyntaxError: parameter without a default
                 follows parameter with a default
    

    because parameters without default values must come before parameters with default values

    this is a problem, I just broke the mary = src.classes.Mary() which was working before.

  • I add a default value for last_name

    53# class Mary(Jane): pass
    54# class Mary(Jane):
    55class Mary(Jane, Joe):
    56# class Mary(Joe, Jane):
    57
    58    # def __init__(self):
    59    # def __init__(self, first_name='mary'):
    60    # def __init__(self, first_name='mary', last_name):
    61    def __init__(
    62            self, first_name='mary',
    63            last_name=None,
    64        ):
    65        # super().__init__('mary')
    66        super().__init__(first_name)
    67        # self.first_name = 'mary'
    68        # self.last_name = 'doe'
    

    because this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil.__init__()
              super().__init__('lil')
          John.__init__('lil')
              super().__init__(first_name)
          Smith.__init__('lil')
              super().__init__(
                  first_name=first_name, last_name='smith',
              )
          Mary.__init__('lil', 'smith')
              super().__init__(first_name)
          Jane.__init__('lil')
              super().__init__(first_name)
              self.eye_color = 'brown'
          Joe.__init__('lil')
              super().__init__('lil')
          Blow.__init__('lil')
              self.first_name = 'lil'
              self.last_name = 'blow'
    
  • I change the order of the parents of Lil to (Mary, John) to see if the value will change to john.last_name

    83# class Lil(John): pass
    84# class Lil(John, Mary): pass
    85# class Lil(John, Mary):
    86class Lil(Mary, John):
    87
    88    def __init__(self):
    89        super().__init__('lil')
    90        # self.first_name = 'lil'
    91        # self.last_name = 'blow'
    

    the test is still green because this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil.__init__()
              super().__init__('lil')
          Mary.__init__('lil')
              Mary.__init__('lil', last_name=None)
              super().__init__(first_name)
          Jane.__init__('lil')
              super().__init__(first_name)
              self.eye_color = 'brown'
          Joe.__init__('lil')
              super().__init__('lil')
          Blow.__init__('lil')
              self.first_name = 'lil'
              self.last_name = 'blow'
    

    no other __init__ methods get called for the parents of Lil

  • I add a call to the super built-in function in Blow

    19class Blow(src.person.Person):
    20
    21    def __init__(self, first_name):
    22        super().__init__(
    23            first_name=first_name,
    24            last_name='blow',
    25        )
    26        # self.first_name = first_name
    27        # self.last_name = 'blow'
    

    the terminal is my friend, and shows TypeError

    TypeError:
        John.__init__() got
        an unexpected keyword argument 'last_name'.
        Did you mean 'first_name'?
    

    because this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil.__init__()
              super().__init__('lil')
          Mary.__init__('lil')
              Mary.__init__('lil', last_name=None)
              super().__init__(first_name)
          Jane.__init__('lil')
              super().__init__(first_name)
              self.eye_color = 'brown'
          Doe.__init__('lil')
              super().__init__(first_name)
          Joe.__init__('lil')
              super().__init__('lil')
          Blow.__init__('lil')
              super().__init__(
                  first_name=first_name,
                  last_name='blow',
              )
          John.__init__(
              first_name='lil',
              last_name='blow',
          )
    

    which raises TypeError since the __init__ method of John does not have a parameter named last_name

  • I add last_name to John

    75# class John(src.person.Person): pass
    76# class John(src.person.Person):
    77class John(Smith):
    78
    79    # def __init__(self):
    80    # def __init__(self, first_name='john'):
    81    def __init__(self, first_name='john', last_name):
    82        # super().__init__('john')
    83        super().__init__(first_name)
    84        # self.first_name = 'john'
    85        # self.last_name = 'smith'
    

    the terminal shows SyntaxError

    SyntaxError: parameter without a default
                 follows parameter with a default
    

    because parameters without default values must come before parameters with default values

  • I give last_name a default value

    75# class John(src.person.Person): pass
    76# class John(src.person.Person):
    77class John(Smith):
    78
    79    # def __init__(self):
    80    # def __init__(self, first_name='john'):
    81    # def __init__(self, first_name='john', last_name):
    82    def __init__(
    83            self, first_name='john',
    84            last_name=None,
    85        ):
    86        # super().__init__('john')
    87        super().__init__(first_name)
    88        # self.first_name = 'john'
    89        # self.last_name = 'smith'
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'smith' != 'blow'
    
  • I change the expectation of the assertion for lil.last_name in test_classes_w_multiple_parents

    190        lil = src.classes.Lil()
    191        self.assertEqual(lil.first_name, 'lil')
    192        # self.assertEqual(lil.last_name, mary.last_name)
    193        self.assertEqual(lil.last_name, john.last_name)
    194        assert issubclass(
    195            src.classes.Lil, src.classes.John
    196        )
    197        # self.assertNotIsSubclass(
    198        self.assertIsSubclass(
    199            src.classes.Lil, src.classes.John
    200        )
    201        assert issubclass(
    202            src.classes.Lil, src.classes.Mary
    203        )
    204
    205
    206# Exceptions seen
    

    the test passes because this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil.__init__()
              super().__init__('lil')
          Mary.__init__('lil')
              Mary.__init__('lil', last_name=None)
              super().__init__(first_name)
          Jane.__init__('lil')
              super().__init__(first_name)
              self.eye_color = 'brown'
          Doe.__init__('lil')
              super().__init__(first_name)
          Joe.__init__('lil')
              super().__init__('lil')
          Blow.__init__('lil')
              super().__init__(
                  first_name=first_name,
                  last_name='blow',
              )
          John.__init__(
              first_name='lil',
              last_name='blow',
          )
              super().__init__(first_name)
          Smith.__init__('lil')
              super().__init__(
                  first_name=first_name,
                  last_name='smith',
              )
          Person.__init__(
              first_name='lil',
              last_name='smith',
          )
              self.first_name = 'lil'
              self.last_name = 'smith'
    

    the order of the parents matters.


  • I add an assertion for lil.eye_color to test if instance of Lil inherit eye_color from Jane, the parent of Mary

    190        lil = src.classes.Lil()
    191        self.assertEqual(lil.first_name, 'lil')
    192        # self.assertEqual(lil.last_name, mary.last_name)
    193        self.assertEqual(lil.last_name, john.last_name)
    194        self.assertEqual(lil.eye_color, '')
    195        assert issubclass(
    196            src.classes.Lil, src.classes.John
    197        )
    198        # self.assertNotIsSubclass(
    199        self.assertIsSubclass(
    200            src.classes.Lil, src.classes.John
    201        )
    202        assert issubclass(
    203            src.classes.Lil, src.classes.Mary
    204        )
    205
    206
    207# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'brown' != ''
    
  • I change the expectation of the assertion

    190        lil = src.classes.Lil()
    191        self.assertEqual(lil.first_name, 'lil')
    192        # self.assertEqual(lil.last_name, mary.last_name)
    193        self.assertEqual(lil.last_name, john.last_name)
    194        # self.assertEqual(lil.eye_color, '')
    195        self.assertEqual(lil.eye_color, jane.eye_color)
    196        assert issubclass(
    197            src.classes.Lil, src.classes.John
    198        )
    199        # self.assertNotIsSubclass(
    200        self.assertIsSubclass(
    201            src.classes.Lil, src.classes.John
    202        )
    203        assert issubclass(
    204            src.classes.Lil, src.classes.Mary
    205        )
    206
    207
    208# Exceptions seen
    

    instances of Lil and Mary get eye_color from Jane if Jane is an ancestor of the first parent in the order of parents.


  • I want to see what happens when I add the eye_color attribute to Mary in classes.py

    57# class Mary(Jane): pass
    58# class Mary(Jane):
    59class Mary(Jane, Joe):
    60# class Mary(Joe, Jane):
    61
    62    # def __init__(self):
    63    # def __init__(self, first_name='mary'):
    64    # def __init__(self, first_name='mary', last_name):
    65    def __init__(
    66            self, first_name='mary',
    67            last_name=None,
    68        ):
    69        # super().__init__('mary')
    70        super().__init__(first_name)
    71        self.eye_color = 'red'
    72        # self.first_name = 'mary'
    73        # self.last_name = 'doe'
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'red' != 'brown'
    

    for mary.eye_color because instances of classes can override the attributes and methods they inherit

  • I change the expectation of the assertion for mary.eye_color in test_classes_w_multiple_parents in test_classes.py

    156        # mary = src.classes.Jane('mary')
    157        mary = src.classes.Mary()
    158        self.assertEqual(mary.first_name, 'mary')
    159        # self.assertEqual(mary.last_name, mary.first_name)
    160        # self.assertEqual(mary.last_name, jane.last_name)
    161        self.assertEqual(mary.last_name, joe.last_name)
    162        # self.assertEqual(mary.eye_color, jane.eye_color)
    163        self.assertEqual(mary.eye_color, 'red')
    164        # assert not issubclass(
    165        assert issubclass(
    166            src.classes.Mary, src.classes.Jane
    167        )
    168        # self.assertNotIsSubclass(
    169        self.assertIsSubclass(
    170            src.classes.Mary, src.classes.Jane
    171        )
    172        assert issubclass(
    173            src.classes.Mary, src.classes.Joe
    174        )
    175        # self.assertNotIsSubclass(
    176        self.assertIsSubclass(
    177            src.classes.Mary, src.classes.Joe
    178        )
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'red' != 'brown'
    

    for lil.eye_color because this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil.__init__()
              super().__init__('lil')
          Mary.__init__('lil')
              Mary.__init__('lil', last_name=None)
              super().__init__(first_name)
              self.eye_color = 'red'
          Jane.__init__('lil')
              super().__init__(first_name)
              # self.eye_color = 'brown' does not affect instance
          Doe.__init__('lil')
              super().__init__(first_name)
          Joe.__init__('lil')
              super().__init__('lil')
          Blow.__init__('lil')
              super().__init__(
                  first_name=first_name,
                  last_name='blow',
              )
          John.__init__(
              first_name='lil',
              last_name='blow',
          )
              super().__init__(first_name)
          Smith.__init__('lil')
              super().__init__(
                  first_name=first_name,
                  last_name='smith',
              )
          Person.__init__(
              first_name='lil',
              last_name='smith',
          )
              self.first_name = 'lil'
              self.last_name = 'smith'
    

    the eye_color attribute of Jane never gets set for instances of Mary

  • I change the expectation of the assertion for lil.eye_color in test_classes_w_multiple_parents

    191        lil = src.classes.Lil()
    192        self.assertEqual(lil.first_name, 'lil')
    193        # self.assertEqual(lil.last_name, mary.last_name)
    194        self.assertEqual(lil.last_name, john.last_name)
    195        # self.assertEqual(lil.eye_color, '')
    196        # self.assertEqual(lil.eye_color, jane.eye_color)
    197        self.assertEqual(lil.eye_color, mary.eye_color)
    198        assert issubclass(
    199            src.classes.Lil, src.classes.John
    200        )
    201        # self.assertNotIsSubclass(
    202        self.assertIsSubclass(
    203            src.classes.Lil, src.classes.John
    204        )
    205        assert issubclass(
    206            src.classes.Lil, src.classes.Mary
    207        )
    208
    209
    210# Exceptions seen
    

    the test passes.

  • I change the order of the parents of Lil in classes.py

     93# class Lil(John): pass
     94# class Lil(John, Mary): pass
     95class Lil(John, Mary):
     96# class Lil(Mary, John):
     97
     98    def __init__(self):
     99        super().__init__('lil')
    100        # self.first_name = 'lil'
    101        # self.last_name = 'blow'
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'blow' != 'smith'
    

    because the order of the parents has an effect on the last_name attribute.

  • I change the expectation of the assertion for lil.last_name

    191        lil = src.classes.Lil()
    192        self.assertEqual(lil.first_name, 'lil')
    193        self.assertEqual(lil.last_name, mary.last_name)
    194        # self.assertEqual(lil.last_name, john.last_name)
    195        # self.assertEqual(lil.eye_color, '')
    196        # self.assertEqual(lil.eye_color, jane.eye_color)
    197        self.assertEqual(lil.eye_color, mary.eye_color)
    198        assert issubclass(
    199            src.classes.Lil, src.classes.John
    200        )
    201        # self.assertNotIsSubclass(
    202        self.assertIsSubclass(
    203            src.classes.Lil, src.classes.John
    204        )
    205        assert issubclass(
    206            src.classes.Lil, src.classes.Mary
    207        )
    

    the test passes.

  • I add an assertion for john.eye_color

    180        john = src.classes.John()
    181        self.assertEqual(john.first_name, 'john')
    182        self.assertEqual(john.last_name, 'smith')
    183        self.assertEqual(john.eye_color, 'orange')
    184        assert issubclass(
    185            src.classes.John, src.classes.Smith
    186        )
    187        # self.assertNotIsSubclass(
    188        self.assertIsSubclass(
    189            src.classes.John, src.classes.Smith
    190        )
    

    the terminal is my friend, and shows AttributeError

    AttributeError: 'John' object has no attribute 'eye_color'
    
  • I add a value for the eye_color attribute to Smith in classes.py

    30class Smith(src.person.Person):
    31
    32    def __init__(self, first_name):
    33        super().__init__(
    34            first_name=first_name,
    35            last_name='smith',
    36        )
    37        self.eye_color = 'orange'
    38        # self.first_name = first_name
    39        # self.last_name = 'smith'
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'orange' != 'red'
    

    because if the parents of Lil are (John, Mary), this happens when lil = src.classes.Lil() runs

    lil = src.classes.Lil()
          Lil.__init__()
              super().__init__('lil')
          John.__init__('lil')
              super().__init__(first_name)
          Smith.__init__('lil')
              super().__init__(
                  first_name=first_name, last_name='smith',
              )
              self.eye_color = 'orange'
          Mary.__init__('lil', 'smith')
              super().__init__(first_name)
              # self.eye_color = 'red' does not affect instance
          Jane.__init__('lil')
              super().__init__(first_name)
              # self.eye_color = 'brown' does not affect instance
          Joe.__init__('lil')
              super().__init__('lil')
          Blow.__init__('lil')
              self.first_name = 'lil'
              self.last_name = 'blow'
    





  • I add an assertion for the first_name, in test_classes_w_multiple_parents in test_classes.py

    164        lil = src.classes.Lil()
    165        self.assertIsInstance(lil, src.classes.John)
    166        self.assertEqual(lil.first_name, 'lil')
    167
    168
    169# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'john' != 'lil'
    
  • I add the __init__ method in the definition of Lil in classes.py

    57# class Lil(John): pass
    58class Lil(John):
    59
    60    def __init__(self, first_name='lil'):
    61        super().__init__(first_name=first_name)
    

    the test passes.

  • I add an assertion for the last name of lil, in test_classes_w_multiple_parents in test_classes.py

    164        lil = src.classes.Lil()
    165        self.assertIsInstance(lil, src.classes.John)
    166        self.assertEqual(lil.first_name, 'lil')
    167        self.assertEqual(lil.last_name, lil.first_name)
    168
    169
    170# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'smith' != 'lil'
    
  • I change the expectation

    164        lil = src.classes.Lil()
    165        self.assertIsInstance(lil, src.classes.John)
    166        self.assertEqual(lil.first_name, 'lil')
    167        # self.assertEqual(lil.last_name, lil.first_name)
    168        self.assertEqual(lil.last_name, john.last_name)
    169
    170
    171# Exceptions seen
    

    the test passes because Python makes the following calls to resolve the call to make an instance of the Lil class when John is the parent

    lil = src.classes.Lil()
          Lil.__init__(first_name='lil')
          super().__init__(first_name=first_name)
        = John.__init__(first_name='lil')
          super().__init__(first_name=first_name)
        = Smith.__init__('lil')
          super().__init__(first_name, last_name='smith')
        = Person.__init__('lil', last_name='smith')
          self.first_name = 'lil'
          self.last_name = 'smith'
    
  • I add assertIsInstance_ to test if Lil is an instance of Mary

    164        lil = src.classes.Lil()
    165        self.assertIsInstance(lil, src.classes.John)
    166        self.assertIsInstance(lil, src.classes.Mary)
    167        self.assertEqual(lil.first_name, 'lil')
    168        # self.assertEqual(lil.last_name, lil.first_name)
    169        self.assertEqual(lil.last_name, john.last_name)
    170
    171
    172# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <src.classes.Lil object at 0xffffaaaaaaaa>
        is not an instance of <class 'src.classes.Mary'>
    

    because Lil is not a child of Mary, yet.

  • I add Mary as a parent of Lil before John to see if it will change the last_name value, in classes.py

    57# class Lil(John): pass
    58# class Lil(John):
    59class Lil(Mary, John):
    60
    61    def __init__(self, first_name='lil'):
    62        super().__init__(first_name=first_name)
    

    the terminal is my friend, and shows TypeError

    TypeError: John.__init__() got
               an unexpected keyword argument 'last_name'.
               Did you mean 'first_name'?
    

    because Python makes the following calls to resolve the call to make an instance of the Lil class if the parents are (Mary, John)

    first it makes these calls for the Mary parent, to resolve the attributes

    lil = src.classes.Lil()
          Lil.__init__(first_name='lil')
          super().__init__(first_name=first_name)
        = Mary.__init__(first_name='lil')
          super().__init__(first_name=first_name)
        = Jane.__init__(first_name='lil', **kwargs)
          super().__init__(first_name=first_name)
        = Doe # Doe has no __init__, skip to Joe
        = Joe.__init__(first_name='lil')
          super().__init__(first_name=first_name)
        = Blow.__init__('lil')
          super().__init__(first_name, last_name='blow')
    

    then it makes this call for the John parent, to resolve the attributes

    lil = John.__init__(first_name='lil', last_name='blow')
    

    which raises TypeError since the __init__ method of John only takes one keyword_argument (first_name) and the call provides two (first_name and last_name)

  • I add a double starred expression to John so it can take any number of keyword arguments

    50# class John(src.person.Person):
    51class John(Smith):
    52
    53    # def __init__(self, first_name='john'):
    54    def __init__(self, first_name='john', **kwargs):
    55        super().__init__(first_name=first_name)
    

    the test passes Python makes this call for the John parent after it gets values from the Mary parent, to resolve the attributes

    lil = John.__init__(first_name='lil', last_name='blow')
          super().__init__(first_name=first_name)
        = Smith.__init__(first_name='lil')
          super().__init__(first_name, last_name='smith')
        = Person.__init__('lil', last_name='smith')
          self.first_name = 'lil'
          self.last_name = 'smith'
    

  • I change the order of the parents of Lil

    58# class Lil(John): pass
    59# class Lil(John):
    60# class Lil(Mary, John):
    61class Lil(John, Mary):
    62
    63    def __init__(self, first_name='lil'):
    64        super().__init__(first_name=first_name)
    

    the terminal is my friend, and shows TypeError

    TypeError: Mary.__init__() got
               an unexpected keyword argument 'last_name'.
               Did you mean 'first_name'?
    

    same problem, same solution

  • I add a double starred expression to Mary so it can take any number of keyword arguments

    41# class Mary(Jane): pass
    42# class Mary(Joe, Jane): pass
    43# class Mary(Joe, Jane):
    44class Mary(Jane, Joe):
    45
    46    # def __init__(self, first_name='mary'):
    47    def __init__(self, first_name='mary', **kwargs):
    48        super().__init__(first_name=first_name)
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'blow' != 'smith'
    

    the order of the parents matters because Python makes the following calls to resolve the call to make an instance of the Lil class if the parents are (John, Mary)

    first it makes these calls for the John parent, to resolve the attributes

    lil = src.classes.Lil()
          Lil.__init__(first_name='lil')
          super().__init__(first_name=first_name)
        = John.__init__(first_name='lil', **kwargs)
          super().__init__(first_name=first_name)
        = Smith.__init__(first_name='lil')
          super().__init__(first_name, last_name='smith')
    

    then it makes these calls for the Mary parent, to resolve the attributes

    lil = Mary.__init__(first_name='lil', **kwargs)
          super().__init__(first_name=first_name)
        = Jane.__init__(first_name='lil', **kwargs)
          super().__init__(first_name=first_name)
        = Doe # Doe has no __init__, skip to Joe
        = Joe.__init__(first_name='lil')
          super().__init__(first_name=first_name)
        = Blow.__init__('lil')
          super().__init__(first_name, last_name='blow')
        = Person.__init__(first_name='lil', last_name='blow')
          self.first_name = 'lil'
          self.last_name = 'blow'
    
    • Jane and Mary do not pass on the value they receive for last_name to their parents when they call super().__init__ which means the method uses the default value for the parameter because it is called without the parameter

    • Blow always calls Person with blow as the value for last_name it does not matter if I send a different value

    • Smith always calls Person with smith as the value for last_name it does not matter if I send a different value

    • whichever parent calls Person last will be the values that the instance gets

  • I change the order of the parents of Lil back to (Mary, John)

    59# class Lil(John): pass
    60# class Lil(John):
    61class Lil(Mary, John):
    62# class Lil(John, Mary):
    63
    64    def __init__(self, first_name='lil'):
    65        super().__init__(first_name=first_name)
    

    the test is green again. The test shows that

    • if the parents of Lil are defined as (Mary, John) the default value for last_name is the value for last_name of John

    • if the parents of Lil are defined as (John, Mary) the default value for last_name is the value for the last_name of Mary

  • I remove the commented lines

    13class Doe(src.person.Person): pass
    14
    15
    16class Blow(src.person.Person):
    17
    18    def __init__(self, first_name):
    19        super().__init__(first_name, last_name='blow')
    20
    21
    22class Smith(src.person.Person):
    23
    24    def __init__(self, first_name):
    25        super().__init__(first_name, last_name='smith')
    26
    27
    28class Jane(Doe):
    29
    30    def __init__(self, first_name='jane', **kwargs):
    31        super().__init__(first_name=first_name)
    32
    33
    34class Joe(Blow):
    35
    36    def __init__(self, first_name='joe'):
    37        super().__init__(first_name=first_name)
    38
    39
    40class Mary(Jane, Joe):
    41
    42    def __init__(self, first_name='mary', **kwargs):
    43        super().__init__(first_name=first_name)
    44
    45
    46class John(Smith):
    47
    48    def __init__(self, first_name='john', **kwargs):
    49        super().__init__(first_name=first_name)
    50
    51
    52class Lil(Mary, John):
    53
    54    def __init__(self, first_name='lil'):
    55        super().__init__(first_name=first_name)
    

  • I remove the commented lines from test_classes_w_multiple_parents in test_classes.py

    131    def test_classes_w_multiple_parents(self):
    132        joe = src.classes.Joe()
    133        self.assertEqual(joe.first_name, 'joe')
    134        self.assertEqual(joe.last_name, 'blow')
    135        self.assertIsInstance(joe, src.classes.Blow)
    136
    137        jane = src.classes.Jane()
    138        self.assertEqual(jane.first_name, 'jane')
    139        self.assertEqual(jane.last_name, 'doe')
    140        self.assertIsInstance(jane, src.classes.Doe)
    141
    142        mary = src.classes.Mary()
    143        self.assertEqual(mary.first_name, 'mary')
    144        self.assertEqual(mary.last_name, joe.last_name)
    145        self.assertIsInstance(mary, src.classes.Jane)
    146        self.assertIsInstance(mary, src.classes.Doe)
    147        self.assertIsInstance(mary, src.classes.Joe)
    148
    149        john = src.classes.John()
    150        self.assertEqual(john.first_name, 'john')
    151        self.assertEqual(john.last_name, 'smith')
    152        self.assertIsInstance(john, src.classes.Smith)
    153
    154        lil = src.classes.Lil()
    155        self.assertIsInstance(lil, src.classes.John)
    156        self.assertIsInstance(lil, src.classes.Mary)
    157        self.assertEqual(lil.first_name, 'lil')
    158        self.assertEqual(lil.last_name, john.last_name)
    159
    160
    161# Exceptions seen
    
  • I add a git commit message in the other terminal

    git commit -am \
    'add test_classes_w_multiple_parents'
    

Note

All the instances could have been made with only the Person class because there was nothing unique about the classes I made it classes.py and it would not have given me the chance to practice making classes with multiple parents and seeing how Python resolves the order - this is called Method Resolution Order

joe = src.classes.Joe()
joe = src.person.Person('joe', last_name='blow')
jane = src.classes.Jane()
jane = src.person.Person('jane')
mary = src.classes.Mary()
mary = src.person.Person('mary', 'blow')
john = src.classes.John()
john = src.person.Person('john', 'smith')
lil = src.person.Lil()
lil = src.person.Person('lil', 'smith')

which would have just been Python making this call to make instances of the Person class

a_name = src.person.Person(first_name, last_name=last_name)
         Person.__init__(first_name, last_name=last_name)
         self.first_name = first_name
         self.last_name = last_name

I can make classes with multiple parents


review

I can make a class with


close the project

  • I close the file(s) I have open in the editor(s)

  • I click in the terminal where the tests are running, 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 person

    cd ..
    

    the terminal shows

    .../pumping_python
    

    I am back in the pumping_python directory


code from the chapter

Do you want to see all the CODE I typed in this chapter?


what is next?

you have gone through a lot of things and know

Would you like to use test classes with parents?


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.