family ties

The tests from everything is an object show that in Python everything inherits. This allows me to make new objects that get their magic powers from other objects.

Making new objects can be easier with Inheritance because I do not have to write things that have already been written again, 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.


what is Python’s Method Resolution Order?

When an instance is made, Python calls every __init__ method of the parent and its parent going through every ancestor until it gets to the last one that is needed to make the instance.


how to make a class with a parent

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

class Child(Parent):

    attribute = SOMETHING

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

questions about family ties

Questions to think about as I go through the chapter


preview

I have these tests by the end of the chapter

  1import src.family_ties
  2import unittest
  3
  4
  5class TestFamilyTies(unittest.TestCase):
  6
  7    def test_making_a_class_w_inheritance(self):
  8        person_class = src.person.Person
  9        doe_class = src.family_ties.Doe
 10        doe_instance = doe_class('the_first')
 11
 12        self.assertNotIsInstance(
 13            doe_class, person_class
 14        )
 15
 16        self.assertNotIsInstance(
 17            doe_class, doe_class
 18        )
 19
 20        self.assertIsSubclass(
 21            doe_class, person_class
 22        )
 23
 24        self.assertIsInstance(
 25            doe_instance, person_class
 26        )
 27
 28        self.assertEqual(
 29            dir(doe_class), dir(person_class)
 30        )
 31
 32    def test_classes_w_one_parent(self):
 33        doe = src.family_ties.Doe('the_first')
 34        self.assertEqual(doe.last_name, 'doe')
 35
 36        joe = src.family_ties.Blow('joe')
 37        self.assertEqual(joe.last_name, 'blow')
 38
 39        blow = src.person.Person('joe', last_name='blow')
 40        self.assertEqual(blow.last_name, joe.last_name)
 41
 42        jane = src.person.Person('jane')
 43        self.assertEqual(jane.last_name, doe.last_name)
 44
 45        john = src.family_ties.Smith('john')
 46        self.assertEqual(john.last_name, 'smith')
 47
 48        smith = src.person.Person('john', 'smith')
 49        self.assertEqual(smith.last_name, john.last_name)
 50
 51    def test_classes_w_multiple_parents(self):
 52        joe = src.family_ties.Joe()
 53        self.assertEqual(joe.first_name, 'joe')
 54        self.assertEqual(joe.last_name, 'blow')
 55        self.assertEqual(joe.eye_color, 'blue')
 56        self.assertIsSubclass(
 57            src.family_ties.Joe, src.family_ties.Blow
 58        )
 59
 60        jane = src.family_ties.Jane()
 61        self.assertEqual(jane.first_name, 'jane')
 62        self.assertEqual(jane.last_name, 'doe')
 63        self.assertEqual(jane.eye_color, 'green')
 64        self.assertIsSubclass(
 65            src.family_ties.Jane, src.family_ties.Doe
 66        )
 67
 68        mary = src.family_ties.Mary()
 69        self.assertEqual(mary.first_name, 'mary')
 70        self.assertEqual(mary.last_name, jane.last_name)
 71        self.assertEqual(mary.eye_color, joe.eye_color)
 72        self.assertIsSubclass(
 73            src.family_ties.Mary, src.family_ties.Jane
 74        )
 75        self.assertIsSubclass(
 76            src.family_ties.Mary, src.family_ties.Joe
 77        )
 78
 79        john = src.family_ties.John()
 80        self.assertEqual(john.first_name, 'john')
 81        self.assertEqual(john.last_name, 'smith')
 82        self.assertEqual(john.eye_color, 'brown')
 83        self.assertIsSubclass(
 84            src.family_ties.John, src.family_ties.Smith
 85        )
 86
 87        lil = src.family_ties.Lil()
 88        self.assertEqual(lil.first_name, 'lil')
 89        self.assertEqual(lil.last_name, john.last_name)
 90        self.assertEqual(lil.eye_color, mary.eye_color)
 91        self.assertIsSubclass(
 92            src.family_ties.Lil, src.family_ties.John
 93        )
 94        self.assertIsSubclass(
 95            src.family_ties.Lil, src.family_ties.Mary
 96        )
 97
 98
 99# Exceptions seen
100# AssertionError
101# NameError
102# AttributeError
103# ModuleNotFoundError
104# 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_family_ties.py

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

    touch src/family_ties.py
    
  • I open test_family_ties.py

  • I add the first failing test to test_family_ties.py

    1import unittest
    2
    3
    4class TestFamilyTies(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 ==========================
    _________________ TestFamilyTies.test_failure __________________
    
    self = <tests.test_family_ties.TestFamilyTies testMethod=test_failure>
    
        def test_failure(self):
    >       self.assertFalse(True)
    E       AssertionError: True is not false
    
    tests/test_family_ties.py:7: AssertionError
    =================== short test summary info ====================
    FAILED tests/test_family_ties.py::TestFamilyTies::test_failure - AssertionError: True is not false
    ================= 1 failed, 6 passed in X.YZs ==================
    
  • I add AssertionError to the list of Exceptions seen, in test_functions.py

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

    7        self.assertFalse(False)
    

    the test passes.


test_making_a_class_w_inheritance

I know from test_making_a_class_w_object that I can make classes with inheritance by stating the parent class and that an instance (a copy) and a subclass (child) are different.


RED: make it fail


  • I go back to the terminal where the tests are running

  • I change test_failure to test_making_a_class_w_inheritance with an assertion

     4class TestFamilyTies(unittest.TestCase):
     5
     6    def test_making_a_class_w_inheritance(self):
     7        self.assertIsInstance(
     8            src.family_ties.Doe,
     9            src.person.Person
    10        )
    11
    12
    13# 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

    4# Exceptions seen
    5# AssertionError
    6# NameError
    

GREEN: make it pass


  • I add an import statement for the family_ties module at the top of the file

    1import src.family_ties
    2import unittest
    3
    4
    5class TestFamilyTies(unittest.TestCase):
    
    • import src.family_ties brings in an object for the family_ties.py module from the src folder so I can use it in test_family_ties.py

    • the terminal is my friend, and shows AttributeError

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

      because there is no definition for Doe in family_ties.py

  • I add AttributeError to the list of Exceptions seen

    14# Exceptions seen
    15# AssertionError
    16# NameError
    17# AttributeError
    
  • I open family_ties.py from the src folder

  • I add a class definition definition to family_ties.py

    1class Doe(object): pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.family_ties.Doe'> is not
        an instance of <class 'src.person.Person'>
    

    because Doe is not an instance (a copy) of Person.

  • I change the assertion in test_making_a_class_w_inheritance in test_family_ties.py

     7    def test_making_a_class_w_inheritance(self):
     8        # self.assertIsInstance(
     9        self.assertNotIsInstance(
    10            src.family_ties.Doe,
    11            src.person.Person
    12        )
    13
    14
    15# Exceptions seen
    

    the test passes. Doe is NOT an instance of the Person class, they are siblings - both children of object.


REFACTOR: make it better


  • I add a call to assertIsSubclass

     7    def test_making_a_class_w_inheritance(self):
     8        self.assertIsInstance(
     9        # self.assertNotIsInstance(
    10            src.family_ties.Doe,
    11            src.person.Person
    12        )
    13
    14        self.assertIsSubclass(
    15            src.family_ties.Doe,
    16            src.person.Person
    17        )
    18
    19
    20# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.family_ties.Doe'> is not a
        subclass of <class 'src.person.Person'>
    

    because Doe is not a child of Person, yet.

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

    1# class Doe(object): pass
    2class 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 family_ties.py

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

    1import person
    2
    3
    4# class Doe(object): pass
    5class Doe(person.Person): pass
    

    the terminal is my friend, and shows ModuleNotFoundError

    E   ModuleNotFoundError: No module named 'person'
    

    because Python cannot find person.py in the main project folder (the parent of src and tests) where I run the tests from, so it cannot import the Module.

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

    20# Exceptions seen
    21# AssertionError
    22# NameError
    23# AttributeError
    24# ModuleNotFoundError
    
  • I add the path of person.py from the main project folder (the parent of src and tests) to the import statement, in family_ties.py

    1# import person
    2import src.person
    3
    4
    5# class Doe(object): pass
    6class Doe(person.Person): pass
    

    the terminal is my friend, it goes back to NameError

    NameError: name 'person' is not defined
    

    because there is no definition for person in this file.

  • I add src. to the parent of Doe

    5# class Doe(object): pass
    6# class Doe(person.Person): pass
    7class Doe(src.person.Person): pass
    
    • import src.person brings in an object for the person.py module from the src folder so I can use it in family_ties.py.

    • I have to use src.person.Person in family_ties.py because I am testing from the root folder of the project (the parent folder of src and tests).

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

    • This is a problem because if family_ties.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 (a problem for another time).

    • The test passes because Doe is now a child (subclass) of Person.


more about instances vs subclasses


  • I add a call to assertIsInstance to show that src.family_ties.Doe is not an instance

     7    def test_making_a_class_w_inheritance(self):
     8        # self.assertIsInstance(
     9        self.assertNotIsInstance(
    10            src.family_ties.Doe,
    11            src.person.Person
    12        )
    13
    14        self.assertIsInstance(
    15            src.family_ties.Doe,
    16            src.family_ties.Doe
    17        )
    18
    19        self.assertIsSubclass(
    20            src.family_ties.Doe,
    21            src.person.Person
    22        )
    23
    24
    25# Exceptions seen
    

    the terminal shows AssertionError

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

    because a class is not an instance.

  • I change assertIsInstance to assertNotIsInstance

     7    def test_making_a_class_w_inheritance(self):
     8        # self.assertIsInstance(
     9        self.assertNotIsInstance(
    10            src.family_ties.Doe,
    11            src.person.Person
    12        )
    13
    14        # self.assertIsInstance(
    15        self.assertNotIsInstance(
    16            src.family_ties.Doe,
    17            src.family_ties.Doe
    18        )
    19
    20        self.assertIsSubclass(
    21            src.family_ties.Doe,
    22            src.person.Person
    23        )
    24
    25
    26# Exceptions seen
    
  • I add a variable

     7    def test_making_a_class_w_inheritance(self):
     8        doe_class = src.family_ties.Doe
     9
    10        # self.assertIsInstance(
    11        self.assertNotIsInstance(
    12            src.family_ties.Doe,
    13            src.person.Person
    14        )
    
  • I use the variable to remove repetition of src.family_ties.Doe from the test

     7    def test_making_a_class_w_inheritance(self):
     8        doe_class = src.family_ties.Doe
     9
    10        # self.assertIsInstance(
    11        self.assertNotIsInstance(
    12            # src.family_ties.Doe,
    13            doe_class,
    14            src.person.Person
    15        )
    16
    17        # self.assertIsInstance(
    18        self.assertNotIsInstance(
    19            # src.family_ties.Doe,
    20            # src.family_ties.Doe
    21            doe_class, doe_class
    22        )
    23
    24        self.assertIsSubclass(
    25            # src.family_ties.Doe,
    26            doe_class,
    27            src.person.Person
    28        )
    29
    30
    31# Exceptions seen
    

    the test is still green.


what happens when a child calls the parent?


  • I add a call to the assertIsInstance method, this time with an instance of Doe

    24        self.assertIsSubclass(
    25            # src.family_ties.Doe,
    26            doe_class,
    27            src.person.Person
    28        )
    29
    30        self.assertIsInstance(
    31            src.family_ties.Doe(),
    32            src.person.Person
    33        )
    34
    35
    36# Exceptions seen
    

    the terminal is my friend, and shows TypeError

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

    I called Doe to make an instance. How did Person.__init__ get called?

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

    src.family_ties.Doe()
    Doe # has no __init__
    # call the parent of Doe (Person)
    Person.__init__()
    

    which raises TypeError since the __init__ method of the Person class takes one required argument for first_name.

  • I add TypeError to the list of Exceptions seen

    36# Exceptions seen
    37# AssertionError
    38# NameError
    39# AttributeError
    40# ModuleNotFoundError
    41# TypeError
    

how to call the parent from the child


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

     5# class Doe(object): pass
     6# class Doe(person.Person): pass
     7# class Doe(src.person.Person): pass
     8class Doe(src.person.Person):
     9
    10    def __init__(self):
    11        super().__init__()
    
    • the super built-in function calls the __init__ method of the parent class

    • super() is the parent - “super class” for parent, “subclass” for child

    • super is Person in this case

    • super().__init__() is Person.__init__() in this case

    • the terminal still shows TypeError

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

      because this is what happens now when src.class.Doe() runs

      src.family_ties.Doe()
      Doe.__init__()
          super().__init__()
      # super is the parent (Person)
      Person.__init__()
      

      which raises TypeError since the __init__ method of the Person class takes one required argument for first_name.

  • I add a value for first_name to the call to src.family_ties.Doe() in test_making_a_class_w_inheritance in test_family_ties.py

    30        self.assertIsInstance(
    31            # src.family_ties.Doe(),
    32            src.family_ties.Doe('the_first'),
    33            src.person.Person
    34        )
    35
    36
    37# 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 src.family_ties.Doe('the_first') runs

    src.family_ties.Doe('the_first')
    Doe.__init__('the_first')
    
  • I add a parameter for first_name to the __init__ method of Doe in family_ties.py

     5# class Doe(object): pass
     6# class Doe(person.Person): pass
     7# class Doe(src.person.Person): pass
     8class Doe(src.person.Person):
     9
    10    # def __init__(self):
    11    def __init__(self, first_name):
    12        super().__init__()
    

    the terminal is my friend, and shows TypeError

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

    because this happens when src.family_ties.Doe('the_first') runs

    src.family_ties.Doe('the_first')
    Doe.__init__('the_first')
        super().__init__() # call the parent
    Person.__init__()
    
  • I add the required parameter to super().__init__() in Doe

     5# class Doe(object): pass
     6# class Doe(person.Person): pass
     7# class Doe(src.person.Person): pass
     8class Doe(src.person.Person):
     9
    10    # def __init__(self):
    11    def __init__(self, first_name):
    12        # super().__init__()
    13        super().__init__(first_name)
    

    the test passes because

    • an instance (a copy) of Doe is an an instance (a copy) of Person

    • Person is the parent of Doe

    • the test shows that this happens when src.family_ties.Doe('the_first') runs

      src.family_ties.Doe('the_first')
      Doe.__init__('the_first')
          super().__init__('the_first')
      Person.__init__('the_first')
      
  • I add a test for the attributes and methods of the Doe class

    30        self.assertIsInstance(
    31            # src.family_ties.Doe(),
    32            src.family_ties.Doe('the_first'),
    33            src.person.Person
    34        )
    35
    36        self.assertEqual(
    37            dir(doe_class),
    38            []
    39        )
    40
    41
    42# 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

    36        self.assertEqual(
    37            dir(doe_class),
    38            # []
    39            dir(src.person.Person)
    40        )
    41
    42
    43# Exceptions seen
    

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

  • I add a variable

    7    def test_making_a_class_w_inheritance(self):
    8        person_class = src.person.Person
    9        doe_class = src.family_ties.Doe
    
  • I use the variable to remove repetition of src.person.Person from the test

     7    def test_making_a_class_w_inheritance(self):
     8        person_class = src.person.Person
     9        doe_class = src.family_ties.Doe
    10
    11        # self.assertIsInstance(
    12        self.assertNotIsInstance(
    13            # src.family_ties.Doe,
    14            doe_class,
    15            # src.person.Person
    16            person_class
    17        )
    18
    19        # self.assertIsInstance(
    20        self.assertNotIsInstance(
    21            # src.family_ties.Doe,
    22            # src.family_ties.Doe
    23            doe_class, doe_class
    24        )
    25
    26        self.assertIsSubclass(
    27            # src.family_ties.Doe,
    28            doe_class,
    29            # src.person.Person
    30            person_class
    31        )
    32
    33        self.assertIsInstance(
    34            # src.family_ties.Doe(),
    35            src.family_ties.Doe('the_first'),
    36            # src.person.Person
    37            person_class
    38        )
    39
    40        self.assertEqual(
    41            dir(doe_class),
    42            # []
    43            # dir(src.person.Person)
    44            dir(person_class)
    45        )
    46
    47
    48# Exceptions seen
    

    still green.

  • I add a variable for src.family_ties.Doe('the_first')

     7    def test_making_a_class_w_inheritance(self):
     8        person_class = src.person.Person
     9        doe_class = src.family_ties.Doe
    10        doe_instance = doe_class('the_first')
    11
    12        # self.assertIsInstance(
    13        self.assertNotIsInstance(
    14            # src.family_ties.Doe,
    15            doe_class,
    16            # src.person.Person
    17            person_class
    18        )
    
  • I use the new variable

    34        self.assertIsInstance(
    35            # src.family_ties.Doe(),
    36            # src.family_ties.Doe('the_first'),
    37            doe_instance,
    38            # src.person.Person
    39            person_class
    40        )
    

    green, because doe_class() and src.classes.Doe() are the same since doe_class = src.classes.Doe.

  • I remove the commented lines

     7    def test_making_a_class_w_inheritance(self):
     8        person_class = src.person.Person
     9        doe_class = src.family_ties.Doe
    10        doe_instance = doe_class('the_first')
    11
    12        self.assertNotIsInstance(
    13            doe_class, person_class
    14        )
    15
    16        self.assertNotIsInstance(
    17            doe_class, doe_class
    18        )
    19
    20        self.assertIsSubclass(
    21            doe_class, person_class
    22        )
    23
    24        self.assertIsInstance(
    25            doe_instance, person_class
    26        )
    27
    28        self.assertEqual(
    29            dir(doe_class), dir(person_class)
    30        )
    31
    32
    33# Exceptions seen
    
  • I remove the commented lines from family_ties.py

    1import src.person
    2
    3
    4class Doe(src.person.Person):
    5
    6    def __init__(self, first_name):
    7        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

I want to test how the attributes of classes are set if they have only one parent (super class).


RED: make it fail


  • I go back to the terminal where the tests are running

  • I add a new test for Inheritance with an assertion, in test_family_ties.py

    28        self.assertEqual(
    29            dir(doe_class), dir(person_class)
    30        )
    31
    32    def test_classes_w_one_parent(self):
    33        doe = src.family_ties.Doe('the_first')
    34        self.assertEqual(doe.last_name, '')
    35
    36
    37# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'doe' != ''
    

GREEN: make it pass


I change the expectation

32    def test_classes_w_one_parent(self):
33        doe = src.family_ties.Doe('the_first')
34        # self.assertEqual(doe.last_name, '')
35        self.assertEqual(doe.last_name, 'doe')
36
37
38# Exceptions seen

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

doe = src.family_ties.Doe('the_first')
      Doe.__init__('the_first')
          super().__init__(first_name)
      Person.__init__('the_first')
          Person.__init__('the_first', last_name='the_first')
          self.last_name = 'the_first' # 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

    32    def test_classes_w_one_parent(self):
    33        doe = src.family_ties.Doe('the_first')
    34        # self.assertEqual(doe.last_name, '')
    35        self.assertEqual(doe.last_name, 'doe')
    36
    37        joe = src.family_ties.Blow('joe')
    38        self.assertEqual(joe.last_name, 'blow')
    39
    40
    41# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: module 'src.family_ties' has no attribute 'Blow'
    

    because there is no definition for Blow in family_ties.py

  • I add a new class definition to family_ties.py

     4class Doe(src.person.Person):
     5
     6    def __init__(self, first_name):
     7        super().__init__(first_name)
     8
     9
    10class Blow(src.person.Person): pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'doe' != 'blow'
    

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

    joe = src.family_ties.Blow('joe')
          Blow # has no __init__
          # call the parent of Blow (Person)
          Person.__init__('joe')
              Person.__init__('joe', last_name='doe')
              self.last_name = 'doe' # use the default value
    
  • I add a class attribute for last_name in the Blow class in family_ties.py

    10# class Blow(src.person.Person): pass
    11class Blow(src.person.Person):
    12
    13    last_name = 'blow'
    

    the terminal does not feel like my friend, it still shows AssertionError because this happens when joe = src.family_ties.Blow('joe') runs

    joe = src.family_ties.Blow('joe')
          Blow # has no __init__
          # call the parent of Blow (Person)
              self.last_name = 'blow'
          Person.__init__('joe')
              Person.__init__('joe', last_name='doe')
              self.last_name = 'doe' # use the default value
    

    the value for last_name does not get sent to the parent (Person.__init__)

  • I add the __init__ method to customize the last name

    10# class Blow(src.person.Person): pass
    11class Blow(src.person.Person):
    12
    13    # last_name = 'blow'
    14
    15    def __init__(self):
    16        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.family_ties.Blow('joe') runs

    joe = src.family_ties.Blow('joe')
          Blow.__init__('joe')
    
  • I add first_name to the parentheses of the __init__ method definition

    10# class Blow(src.person.Person): pass
    11class Blow(src.person.Person):
    12
    13    # last_name = 'blow'
    14
    15    # def __init__(self):
    16    def __init__(self, first_name):
    17        self.last_name = 'blow'
    

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

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

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

  • I remove the commented lines

     4class Doe(src.person.Person):
     5
     6    def __init__(self, first_name):
     7        super().__init__(first_name)
     8
     9
    10class Blow(src.person.Person):
    11
    12    def __init__(self, first_name):
    13        self.last_name = 'blow'
    
  • In this case there is a simpler way to make joe and doe. I could pass the values to the Person class directly, 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_family_ties.py

    32    def test_classes_w_one_parent(self):
    33        doe = src.family_ties.Doe('the_first')
    34        # self.assertEqual(doe.last_name, '')
    35        self.assertEqual(doe.last_name, 'doe')
    36
    37        joe = src.family_ties.Blow('joe')
    38        self.assertEqual(joe.last_name, 'blow')
    39
    40        blow = src.person.Person('joe')
    41        self.assertEqual(blow.last_name, joe.last_name)
    42
    43
    44# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

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

    40        # blow = src.person.Person('joe')
    41        blow = src.person.Person('joe', last_name='blow')
    42        self.assertEqual(blow.last_name, joe.last_name)
    43
    44
    45# Exceptions seen
    

    the test passes. I can make an instance and change the values of its attributes without making a new class.

  • I add an assertion for jane

    40        # blow = src.person.Person('joe')
    41        blow = src.person.Person('joe', last_name='blow')
    42        self.assertEqual(blow.last_name, joe.last_name)
    43
    44        jane = src.person.Person('jane')
    45        self.assertEqual(jane.last_name, blow.last_name)
    46
    47
    48# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

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

    44        jane = src.person.Person('jane')
    45        # self.assertEqual(jane.last_name, blow.last_name)
    46        self.assertEqual(jane.last_name, doe.last_name)
    47
    48
    49# Exceptions seen
    

    the test passes.

  • I add an assertion for john

    44        jane = src.person.Person('jane')
    45        # self.assertEqual(jane.last_name, blow.last_name)
    46        self.assertEqual(jane.last_name, doe.last_name)
    47
    48        john = src.family_ties.Smith('john')
    49        self.assertEqual(john.last_name, 'smith')
    50
    51
    52# Exceptions seen
    

    the terminal shows AttributeError

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

    10class Blow(src.person.Person):
    11
    12    def __init__(self, first_name):
    13        self.last_name = 'blow'
    14
    15
    16class Smith(src.person.Person): pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'doe' != 'smith'
    

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

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

    16# class Smith(src.person.Person): pass
    17class Smith(src.person.Person):
    18
    19    def __init__(self):
    20        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.family_ties.Smith('john') runs

    john = src.family_ties.Smith('john')
           Smith.__init__('john')
    
  • I add first_name to the parentheses of the __init__ method

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

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

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

    10class Blow(src.person.Person):
    11
    12    def __init__(self, first_name):
    13        self.last_name = 'blow'
    14
    15
    16class Smith(src.person.Person):
    17
    18    def __init__(self, first_name):
    19        self.last_name = 'smith'
    

    the test passes.

  • I add another assertion to test_classes_w_one_parent in test_family_ties.py

    48        john = src.family_ties.Smith('john')
    49        self.assertEqual(john.last_name, 'smith')
    50
    51        smith = src.person.Person('john', 'smith')
    52        self.assertEqual(smith.last_name, doe.last_name)
    53
    54
    55# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

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

    51        smith = src.person.Person('john', 'smith')
    52        # self.assertEqual(smith.last_name, doe.last_name)
    53        self.assertEqual(smith.last_name, john.last_name)
    54
    55
    56# Exceptions seen
    

    the test passes.

  • I remove the commented lines

    32    def test_classes_w_one_parent(self):
    33        doe = src.family_ties.Doe('the_first')
    34        self.assertEqual(doe.last_name, 'doe')
    35
    36        joe = src.family_ties.Blow('joe')
    37        self.assertEqual(joe.last_name, 'blow')
    38
    39        blow = src.person.Person('joe', last_name='blow')
    40        self.assertEqual(blow.last_name, joe.last_name)
    41
    42        jane = src.person.Person('jane')
    43        self.assertEqual(jane.last_name, doe.last_name)
    44
    45        john = src.family_ties.Smith('john')
    46        self.assertEqual(john.last_name, 'smith')
    47
    48        smith = src.person.Person('john', 'smith')
    49        self.assertEqual(smith.last_name, john.last_name)
    50
    51
    52# Exceptions seen
    

    it will use the default value for last_name if no value is given because a function uses the default value for the parameter because it is called without the parameter.

  • 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? How are the attributes set if they have more than one parent (super class)?


RED: make it fail


  • I go back to the terminal where the tests are running

  • I add a test with an assertion for jane

    48        smith = src.person.Person('john', 'smith')
    49        self.assertEqual(smith.last_name, john.last_name)
    50
    51    def test_classes_w_multiple_parents(self):
    52        jane = src.family_ties.Jane()
    53        self.assertEqual(jane.first_name, 'jane')
    54
    55
    56# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

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

GREEN: make it pass


  • I add a class definition for Jane to family_ties.py

    16class Smith(src.person.Person):
    17
    18    def __init__(self, first_name):
    19        self.last_name = 'smith'
    20
    21
    22class 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.family_ties.Jane() runs

    jane = src.family_ties.Jane()
           Jane # has no __init__
           # call the parent of Jane (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

    22# class Jane(src.person.Person): pass
    23class Jane(src.person.Person):
    24
    25    def __init__(self):
    26        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

    22# class Jane(src.person.Person): pass
    23class Jane(src.person.Person):
    24
    25    def __init__(self):
    26        self.first_name = 'jane'
    27        return None
    

the test passes.


REFACTOR: make it better


  • I add an assertion for the value of the last_name attribute of jane to test_classes_w_multiple_parents in test_family_ties.py

    51    def test_classes_w_multiple_parents(self):
    52        jane = src.family_ties.Jane()
    53        self.assertEqual(jane.first_name, 'jane')
    54        self.assertEqual(jane.last_name, 'doe')
    55
    56
    57# 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 family_ties.py

    22# class Jane(src.person.Person): pass
    23class Jane(src.person.Person):
    24
    25    def __init__(self):
    26        self.first_name = 'jane'
    27        self.last_name = 'doe'
    28        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_family_ties.py

    51    def test_classes_w_multiple_parents(self):
    52        jane = src.family_ties.Jane()
    53        self.assertEqual(jane.first_name, 'jane')
    54        self.assertEqual(jane.last_name, 'doe')
    55        self.assertIsSubclass(
    56            src.family_ties.Jane, src.family_ties.Doe
    57        )
    58
    59
    60# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

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

    22# class Jane(src.person.Person): pass
    23# class Jane(src.person.Person):
    24class Jane(Doe):
    25
    26    def __init__(self):
    27        self.first_name = 'jane'
    28        self.last_name = 'doe'
    29        return None
    

    the test passes.

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

    22# class Jane(src.person.Person): pass
    23# class Jane(src.person.Person):
    24class Jane(Doe):
    25
    26    def __init__(self):
    27        super().__init__()
    28        # self.first_name = 'jane'
    29        # self.last_name = 'doe'
    30        return None
    

    the terminal shows TypeError

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

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

    jane = src.family_ties.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

    22# class Jane(src.person.Person): pass
    23# class Jane(src.person.Person):
    24class Jane(Doe):
    25
    26    def __init__(self):
    27        # super().__init__()
    28        super().__init__('jane')
    29        # self.first_name = 'jane'
    30        # self.last_name = 'doe'
    31        return None
    

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

    jane = src.family_ties.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
    

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


  • I add an assertion for mary, another instance of Jane to test_classes_w_multiple_parents in test_family_ties.py

    51    def test_classes_w_multiple_parents(self):
    52        jane = src.family_ties.Jane()
    53        self.assertEqual(jane.first_name, 'jane')
    54        self.assertEqual(jane.last_name, 'doe')
    55        self.assertIsSubclass(
    56            src.family_ties.Jane, src.family_ties.Doe
    57        )
    58
    59        mary = src.family_ties.Jane('mary')
    60        self.assertEqual(mary.first_name, 'mary')
    61
    62
    63# 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.family_ties.Jane('mary') runs

    mary = src.family_ties.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 family_ties.py

    22# class Jane(src.person.Person): pass
    23# class Jane(src.person.Person):
    24class Jane(Doe):
    25
    26    # def __init__(self):
    27    def __init__(self, first_name):
    28        # super().__init__()
    29        super().__init__('jane')
    30        # self.first_name = 'jane'
    31        # self.last_name = 'doe'
    32        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.family_ties.Jane() runs

    jane = src.family_ties.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

    22# class Jane(src.person.Person): pass
    23# class Jane(src.person.Person):
    24class Jane(Doe):
    25
    26    # def __init__(self):
    27    # def __init__(self, first_name):
    28    def __init__(self, first_name='jane'):
    29        # super().__init__()
    30        super().__init__('jane')
    31        # self.first_name = 'jane'
    32        # self.last_name = 'doe'
    33        return None
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'jane' != 'mary'
    

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

    mary = src.family_ties.Jane('mary')
           Jane.__init__('mary')
               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'
    

    The parent of Jane (Doe) gets called with a different value for the first_name parameter.

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

    22# class Jane(src.person.Person): pass
    23# class Jane(src.person.Person):
    24class Jane(Doe):
    25
    26    # def __init__(self):
    27    # def __init__(self, first_name):
    28    def __init__(self, first_name='jane'):
    29        # super().__init__()
    30        # super().__init__('jane')
    31        super().__init__(first_name)
    32        # self.first_name = 'jane'
    33        # self.last_name = 'doe'
    34        return
    

    the test passes.

  • I remove the commented lines and return None

    16class Smith(src.person.Person):
    17
    18    def __init__(self, first_name):
    19        self.last_name = 'smith'
    20
    21
    22class Jane(Doe):
    23
    24    def __init__(self, first_name='jane'):
    25        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_family_ties.py

    59        mary = src.family_ties.Jane('mary')
    60        self.assertEqual(mary.first_name, 'mary')
    61        self.assertEqual(mary.last_name, mary.first_name)
    62
    63
    64# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

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

    59        mary = src.family_ties.Jane('mary')
    60        self.assertEqual(mary.first_name, 'mary')
    61        # self.assertEqual(mary.last_name, mary.first_name)
    62        self.assertEqual(mary.last_name, jane.last_name)
    63
    64
    65# Exceptions seen
    

    the test passes because mary and jane are instances of Jane


  • I add an assertion for joe

    51    def test_classes_w_multiple_parents(self):
    52        joe = src.family_ties.Joe()
    53        self.assertEqual(joe.first_name, 'joe')
    54
    55        jane = src.family_ties.Jane()
    56        self.assertEqual(jane.first_name, 'jane')
    57        self.assertEqual(jane.last_name, 'doe')
    58        self.assertIsSubclass(
    59            src.family_ties.Jane, src.family_ties.Doe
    60        )
    61
    62        mary = src.family_ties.Jane('mary')
    63        self.assertEqual(mary.first_name, 'mary')
    64        # self.assertEqual(mary.last_name, mary.first_name)
    65        self.assertEqual(mary.last_name, jane.last_name)
    66
    67
    68# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

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

    22class Jane(Doe):
    23
    24    def __init__(self, first_name='jane'):
    25        super().__init__(first_name)
    26
    27
    28class 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.family_ties.Joe() runs

    joe = src.family_ties.Joe()
          Person.__init__()
    
  • I add the __init__ method to Joe

    28# class Joe(src.person.Person): pass
    29class Joe(src.person.Person):
    30
    31    def __init__(self):
    32        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

    28# class Joe(src.person.Person): pass
    29class Joe(src.person.Person):
    30
    31    def __init__(self):
    32        self.first_name = 'joe'
    33        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_family_ties.py

    51    def test_classes_w_multiple_parents(self):
    52        joe = src.family_ties.Joe()
    53        self.assertEqual(joe.first_name, 'joe')
    54        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 family_ties.py

    28# class Joe(src.person.Person): pass
    29class Joe(src.person.Person):
    30
    31    def __init__(self):
    32        self.first_name = 'joe'
    33        self.last_name = 'blow'
    34        return None
    

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

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

    51    def test_classes_w_multiple_parents(self):
    52        joe = src.family_ties.Joe()
    53        self.assertEqual(joe.first_name, 'joe')
    54        self.assertEqual(joe.last_name, 'blow')
    55        self.assertIsSubclass(
    56            src.family_ties.Joe, src.family_ties.Blow
    57        )
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.family_ties.Joe'> is
        not a subclass of <class 'src.family_ties.Blow'>
    
  • I change the parent of Joe to Blow in family_ties.py

    28# class Joe(src.person.Person): pass
    29# class Joe(src.person.Person):
    30class Joe(Blow):
    31
    32    def __init__(self):
    33        self.first_name = 'joe'
    34        self.last_name = 'blow'
    35        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

    28# class Joe(src.person.Person): pass
    29# class Joe(src.person.Person):
    30class Joe(Blow):
    31
    32    def __init__(self):
    33        super().__init__('joe')
    34        # self.first_name = 'joe'
    35        # self.last_name = 'blow'
    36        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.family_ties.Joe() runs

    joe = src.family_ties.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

    10class Blow(src.person.Person):
    11
    12    def __init__(self, first_name):
    13        self.first_name = first_name
    14        self.last_name = 'blow'
    

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

    joe = src.family_ties.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

    23class Jane(Doe):
    24
    25    def __init__(self, first_name='jane'):
    26        super().__init__(first_name)
    27
    28
    29class Joe(Blow):
    30
    31    def __init__(self):
    32        super().__init__('joe')
    

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

    66        # mary = src.family_ties.Jane('mary')
    67        mary = src.family_ties.Mary()
    68        self.assertEqual(mary.first_name, 'mary')
    69        # self.assertEqual(mary.last_name, mary.first_name)
    70        self.assertEqual(mary.last_name, jane.last_name)
    71
    72
    73# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

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

    29class Joe(Blow):
    30
    31    def __init__(self):
    32        super().__init__('joe')
    33
    34
    35class Mary(Jane): pass
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'jane' != 'mary'
    

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

    mary = src.family_ties.Mary()
           Mary # has no __init__
           # call the parent of Mary (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

    35# class Mary(Jane): pass
    36class Mary(Jane):
    37
    38    def __init__(self):
    39        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.family_ties.Mary() runs

    mary = src.family_ties.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.family_ties.Mary() runs

    mary = src.family_ties.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 a call to the assertNotIsSubclass method to test_classes_w_multiple_parents in test_family_ties.py

    66        # mary = src.family_ties.Jane('mary')
    67        mary = src.family_ties.Mary()
    68        self.assertEqual(mary.first_name, 'mary')
    69        # self.assertEqual(mary.last_name, mary.first_name)
    70        self.assertEqual(mary.last_name, jane.last_name)
    71        self.assertNotIsSubclass(
    72            src.family_ties.Mary, src.family_ties.Jane
    73        )
    74
    75
    76# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.family_ties.Mary'> is
        a subclass of <class 'src.family_ties.Jane'>
    
  • I change assertNotIsSubclass to the assertIsSubclass method

    66        # mary = src.family_ties.Jane('mary')
    67        mary = src.family_ties.Mary()
    68        self.assertEqual(mary.first_name, 'mary')
    69        # self.assertEqual(mary.last_name, mary.first_name)
    70        self.assertEqual(mary.last_name, jane.last_name)
    71        # self.assertNotIsSubclass(
    72        self.assertIsSubclass(
    73            src.family_ties.Mary, src.family_ties.Jane
    74        )
    75
    76
    77# Exceptions seen
    

    the test passes.


what happens when a child has more than one parent?


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

    66        # mary = src.family_ties.Jane('mary')
    67        mary = src.family_ties.Mary()
    68        self.assertEqual(mary.first_name, 'mary')
    69        # self.assertEqual(mary.last_name, mary.first_name)
    70        self.assertEqual(mary.last_name, jane.last_name)
    71        # self.assertNotIsSubclass(
    72        self.assertIsSubclass(
    73            src.family_ties.Mary, src.family_ties.Jane
    74        )
    75        self.assertIsSubclass(
    76            src.family_ties.Mary, src.family_ties.Joe
    77        )
    78
    79
    80# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.family_ties.Mary'> is not
        a subclass of <class 'src.family_ties.Joe'>
    

    because Mary is not a child (subclass) of Joe.

  • I add Joe as a parent of Mary in family_ties.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.family_ties.Mary() runs

    mary = src.family_ties.Mary()
           Mary.__init__()
               super().__init__('mary')
           Jane.__init__('mary')
               super().__init__(first_name)
           Doe.__init__('mary')
               super().__init__(first_name)
           # call the next parent of Mary
           Joe.__init__('mary')
    
  • I change the __init__ method of Joe to take a first_name argument

    29class Joe(Blow):
    30
    31    # def __init__(self):
    32    def __init__(self, first_name):
    33        super().__init__('joe')
    

    the terminal is my friend, and shows TypeError

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

    I broke the joe = src.family_ties.Joe() call 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 mary.first_name because this happens when mary = src.family_ties.Mary() runs

    mary = src.family_ties.Mary()
           Mary.__init__()
               super().__init__('mary')
           Jane.__init__('mary')
               super().__init__(first_name)
           Doe.__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

    29    class Joe(Blow):
    30
    31        # def __init__(self):
    32        # def __init__(self, first_name):
    33        def __init__(self, first_name='joe'):
    34            # super().__init__('joe')
    35            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_family_ties.py

    66        # mary = src.family_ties.Jane('mary')
    67        mary = src.family_ties.Mary()
    68        self.assertEqual(mary.first_name, 'mary')
    69        # self.assertEqual(mary.last_name, mary.first_name)
    70        # self.assertEqual(mary.last_name, jane.last_name)
    71        self.assertEqual(mary.last_name, joe.last_name)
    72        # self.assertNotIsSubclass(
    73        self.assertIsSubclass(
    74            src.family_ties.Mary, src.family_ties.Jane
    75        )
    76        self.assertIsSubclass(
    77            src.family_ties.Mary, src.family_ties.Joe
    78        )
    79
    80
    81# 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 family_ties.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.family_ties.Mary() runs

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

    the __init__ method of Jane did not get called even though it is a parent of Mary

  • I add an assertion to show this in test_classes_w_multiple_parents in test_family_ties.py

    59        jane = src.family_ties.Jane()
    60        self.assertEqual(jane.first_name, 'jane')
    61        self.assertEqual(jane.last_name, 'doe')
    62        self.assertEqual(jane.eye_color, 'green')
    63        self.assertIsSubclass(
    64            src.family_ties.Jane, src.family_ties.Doe
    65        )
    66
    67        # mary = src.family_ties.Jane('mary')
    

    the terminal is my friend, and shows AssertionError

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

    23class Jane(Doe):
    24
    25    def __init__(self, first_name='jane'):
    26        super().__init__(first_name)
    27        self.eye_color = 'green'
    

    the test passes.

  • I add an assertion for the eye_color attribute of Mary in test_classes_w_multiple_parents in test_family_ties.py

    67        # mary = src.family_ties.Jane('mary')
    68        mary = src.family_ties.Mary()
    69        self.assertEqual(mary.first_name, 'mary')
    70        # self.assertEqual(mary.last_name, mary.first_name)
    71        # self.assertEqual(mary.last_name, jane.last_name)
    72        self.assertEqual(mary.last_name, joe.last_name)
    73        self.assertEqual(mary.eye_color, jane.eye_color)
    74        # self.assertNotIsSubclass(
    75        self.assertIsSubclass(
    76            src.family_ties.Mary, src.family_ties.Jane
    77        )
    78        self.assertIsSubclass(
    79            src.family_ties.Mary, src.family_ties.Joe
    80        )
    81
    82
    83# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

    AttributeError: 'Mary' object has no attribute 'eye_color'
    

    because the __init__ method of Jane did not get called.

  • I change the order of the parents of Mary from (Joe, Jane) back to (Jane, Joe) in family_ties.py

    39# class Mary(Jane): pass
    40# class Mary(Jane):
    41class Mary(Jane, Joe):
    42# class Mary(Joe, Jane):
    43
    44    def __init__(self):
    45        super().__init__('mary')
    46        # self.first_name = 'mary'
    47        # self.last_name = 'doe'
    

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

    mary = src.family_ties.Mary()
           Mary.__init__()
               super().__init__('mary')
           Jane.__init__('mary')
               super().__init__(first_name)
               self.eye_color = 'green'
           Doe.__init__('mary')
               super().__init__(first_name)
           Joe.__init__('mary')
               super().__init__('joe')
           Blow.__init__('mary')
               self.first_name = 'mary'
               self.last_name = 'blow'
    

    The order of the parents matters.

  • I change the order of the parents to (Joe, Jane) again

    39# class Mary(Jane): pass
    40# class Mary(Jane):
    41# class Mary(Jane, Joe):
    42class Mary(Joe, Jane):
    43
    44    def __init__(self):
    45        super().__init__('mary')
    46        # self.first_name = 'mary'
    47        # self.last_name = 'doe'
    

    the terminal is my friend, and shows AttributeError

    AttributeError: 'Mary' object has no attribute 'eye_color'
    
  • I add the eye_color class attribute to Joe

    30class Joe(Blow):
    31
    32    # def __init__(self):
    33    # def __init__(self, first_name):
    34    def __init__(self, first_name='joe'):
    35        # super().__init__('joe')
    36        super().__init__(first_name)
    37        self.eye_color = 'blue'
    

    the terminal is my friend, and shows AssertionError

    AssertionError: 'blue' != 'green'
    
  • I change the expectation of the assertion for mary.eye_color in test_classes_w_multiple_parents in test_family_ties.py

    67        # mary = src.family_ties.Jane('mary')
    68        mary = src.family_ties.Mary()
    69        self.assertEqual(mary.first_name, 'mary')
    70        # self.assertEqual(mary.last_name, mary.first_name)
    71        # self.assertEqual(mary.last_name, jane.last_name)
    72        self.assertEqual(mary.last_name, joe.last_name)
    73        # self.assertEqual(mary.eye_color, jane.eye_color)
    74        self.assertEqual(mary.eye_color, joe.eye_color)
    75        # self.assertNotIsSubclass(
    76        self.assertIsSubclass(
    77            src.family_ties.Mary, src.family_ties.Jane
    78        )
    79        self.assertIsSubclass(
    80            src.family_ties.Mary, src.family_ties.Joe
    81        )
    82
    83
    84# Exceptions seen
    

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

    mary = src.family_ties.Mary()
           Mary.__init__()
               super().__init__('mary')
           Joe.__init__('mary')
               super().__init__(first_name)
               self.eye_color = 'blue'
           Blow.__init__('mary')
               self.first_name = 'mary'
               self.last_name = 'blow'
    

    The order of the parents matters.

  • I remove the commented lines from Joe and Mary in family_ties.py

    23class Jane(Doe):
    24
    25    def __init__(self, first_name='jane'):
    26        super().__init__(first_name)
    27        self.eye_color = 'green'
    28
    29
    30class Joe(Blow):
    31
    32    def __init__(self, first_name='joe'):
    33        super().__init__(first_name)
    34        self.eye_color = 'blue'
    35
    36
    37class Mary(Joe, Jane):
    38
    39    def __init__(self):
    40        super().__init__('mary')
    

  • I add john to test_classes_w_multiple_parents in test_family_ties.py

    67        # mary = src.family_ties.Jane('mary')
    68        mary = src.family_ties.Mary()
    69        self.assertEqual(mary.first_name, 'mary')
    70        # self.assertEqual(mary.last_name, mary.first_name)
    71        # self.assertEqual(mary.last_name, jane.last_name)
    72        self.assertEqual(mary.last_name, joe.last_name)
    73        # self.assertEqual(mary.eye_color, jane.eye_color)
    74        self.assertEqual(mary.eye_color, joe.eye_color)
    75        # self.assertNotIsSubclass(
    76        self.assertIsSubclass(
    77            src.family_ties.Mary, src.family_ties.Jane
    78        )
    79        self.assertIsSubclass(
    80            src.family_ties.Mary, src.family_ties.Joe
    81        )
    82
    83        john = src.family_ties.John()
    84        self.assertEqual(john.first_name, 'john')
    85
    86
    87# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

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

    37class Mary(Joe, Jane):
    38
    39    def __init__(self):
    40        super().__init__('mary')
    41
    42
    43class 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.family_ties.John() runs

    john = src.family_ties.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 family_ties.py

    43# class John(src.person.Person): pass
    44class John(src.person.Person):
    45
    46    def __init__(self):
    47        self.first_name = 'john'
    

    the test passes.

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

    83        john = src.family_ties.John()
    84        self.assertEqual(john.first_name, 'john')
    85        self.assertIsSubclass(
    86            src.family_ties.John, src.family_ties.Smith
    87        )
    88
    89
    90# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError:
        <class 'src.family_ties.John'> is
        a subclass of <class 'src.family_ties.Smith'>
    

    no cheating this time.

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

    43# class John(src.person.Person): pass
    44# class John(src.person.Person):
    45class John(Smith):
    46
    47    def __init__(self):
    48        self.first_name = 'john'
    

    the test passes.

  • I add an assertion for the last_name attribute

    83        john = src.family_ties.John()
    84        self.assertEqual(john.first_name, 'john')
    85        self.assertEqual(john.last_name, 'smith')
    86        self.assertIsSubclass(
    87            src.family_ties.John, src.family_ties.Smith
    88        )
    89
    90
    91# 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 to John in family_ties.py

    43# class John(src.person.Person): pass
    44# class John(src.person.Person):
    45class John(Smith):
    46
    47    def __init__(self):
    48        self.first_name = 'john'
    49        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

    43# class John(src.person.Person): pass
    44# class John(src.person.Person):
    45class John(Smith):
    46
    47    def __init__(self):
    48        super().__init__('john')
    49        # self.first_name = 'john'
    50        # 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 first_name attribute to Smith

    17class Smith(src.person.Person):
    18
    19    def __init__(self, first_name):
    20        self.first_name = first_name
    21        self.last_name = 'smith'
    

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

    john = src.family_ties.John()
           John.__init__()
               super().__init__('john')
           Smith.__init__('john')
               self.first_name = 'john'
               self.last_name = 'smith'
    
  • I remove the commented lines from John

    38class Mary(Joe, Jane):
    39
    40    def __init__(self):
    41        super().__init__('mary')
    42
    43
    44class John(Smith):
    45
    46    def __init__(self):
    47        super().__init__('john')
    

  • I add lil, an instance of a child (subclass) of John to test_classes_w_multiple_parents in test_family_ties.py

    83        john = src.family_ties.John()
    84        self.assertEqual(john.first_name, 'john')
    85        self.assertEqual(john.last_name, 'smith')
    86        self.assertIsSubclass(
    87            src.family_ties.John, src.family_ties.Smith
    88        )
    89
    90        lil = src.family_ties.Lil()
    91        self.assertIsSubclass(
    92            src.family_ties.Lil, src.family_ties.John
    93        )
    94
    95
    96# Exceptions seen
    

    the terminal is my friend, and shows AttributeError

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

    44class John(Smith):
    45
    46    def __init__(self):
    47        super().__init__('john')
    48
    49
    50class Lil(John): pass
    

    the test passes.

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

    90        lil = src.family_ties.Lil()
    91        self.assertIsSubclass(
    92            src.family_ties.Lil, src.family_ties.John
    93        )
    94        self.assertIsSubclass(
    95            src.family_ties.Lil, src.family_ties.Mary
    96        )
    97
    98
    99# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

    AssertionError: <class 'src.family_ties.Lil'> is not
                    a subclass of <class 'src.family_ties.Mary'>
    
  • I add Mary as a parent to Lil in family_ties.py

    50# class Lil(John): pass
    51class Lil(John, Mary): pass
    

    the test passes.

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

     90        lil = src.family_ties.Lil()
     91        self.assertEqual(lil.first_name, 'lil')
     92        self.assertIsSubclass(
     93            src.family_ties.Lil, src.family_ties.John
     94        )
     95        self.assertIsSubclass(
     96            src.family_ties.Lil, src.family_ties.Mary
     97        )
     98
     99
    100# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: 'john' != 'lil'
    

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

    lil = src.family_ties.Lil()
          Lil # has no __init__
          # call the parent of Lil (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 into Lil in family_ties.py

    50# class Lil(John): pass
    51# class Lil(John, Mary): pass
    52class Lil(John, Mary):
    53
    54    def __init__(self):
    55        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_family_ties.py

     90        lil = src.family_ties.Lil()
     91        self.assertEqual(lil.first_name, 'lil')
     92        self.assertEqual(lil.last_name, john.last_name)
     93        self.assertIsSubclass(
     94            src.family_ties.Lil, src.family_ties.John
     95        )
     96        self.assertIsSubclass(
     97            src.family_ties.Lil, src.family_ties.Mary
     98        )
     99
    100
    101# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

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

    50# class Lil(John): pass
    51# class Lil(John, Mary): pass
    52class Lil(John, Mary):
    53
    54    def __init__(self):
    55        self.first_name = 'lil'
    56        self.last_name = 'smith'
    

    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

    50# class Lil(John): pass
    51# class Lil(John, Mary): pass
    52class Lil(John, Mary):
    53
    54    def __init__(self):
    55        super().__init__('lil')
    56        # self.first_name = 'lil'
    57        # self.last_name = 'smith'
    

    the terminal is my friend, and shows TypeError

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

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

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

    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

    44class John(Smith):
    45
    46    # def __init__(self):
    47    def __init__(self, first_name='john'):
    48        # super().__init__('john')
    49        super().__init__(first_name)
    

    the terminal shows AssertionError

    AssertionError: 'smith' != 'blow'
    

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

    lil = src.family_ties.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
    

    the __init__ method of Mary did not get called.

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

    17class Smith(src.person.Person):
    18
    19    def __init__(self, first_name):
    20        super().__init__(
    21            first_name=first_name,
    22            last_name='smith',
    23        )
    24        # self.first_name = first_name
    25        # 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.family_ties.Lil() runs

    lil = src.family_ties.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',
              )
          # call the next parent of Lil
          Mary.__init__(
              first_name='lil',
              last_name='smith'
          )
    
  • I add first_name with a default value to the __init__ method of Mary

    42class Mary(Joe, Jane):
    43
    44    # def __init__(self):
    45    def __init__(self, first_name='mary'):
    46        # super().__init__('mary')
    47        super().__init__(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'?
    
  • I add last_name to the __init__ method

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

    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 add a default value for last_name

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

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

    lil = src.family_ties.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)
              # no last_name passed to Parent
          Joe.__init__('lil')
              super().__init__(first_name)
              self.eye_color = 'blue'
          Blow.__init__('lil')
              self.first_name = 'lil'
              self.last_name = 'blow'
    

    the __init__ method of Jane did not get called.

  • I change the order of the parents of Lil to (Mary, John) to see if the value will change to john.last_name

    63# class Lil(John): pass
    64# class Lil(John, Mary): pass
    65# class Lil(John, Mary):
    66class Lil(Mary, John):
    67
    68    def __init__(self):
    69        super().__init__('lil')
    70        # self.first_name = 'lil'
    71        # self.last_name = 'smith'
    

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

    lil = src.family_ties.Lil()
          Lil.__init__()
              super().__init__('lil')
          Mary.__init__('lil')
              Mary.__init__('lil', last_name=None)
              # use the default value
              super().__init__(first_name)
              # no last_name passed to Parent
          Joe.__init__('lil')
              super().__init__(first_name)
              self.eye_color = 'blue'
          Blow.__init__('lil')
              self.first_name = 'lil'
              self.last_name = 'blow'
    

    the __init__ method of Jane did not get called.

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

    10class Blow(src.person.Person):
    11
    12    def __init__(self, first_name):
    13        super().__init__(
    14            first_name=first_name,
    15            last_name='blow',
    16        )
    17        # self.first_name = first_name
    18        # self.last_name = 'blow'
    

    the terminal is my friend, and shows TypeError

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

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

    lil = src.family_ties.Lil()
          Lil.__init__()
              super().__init__('lil')
          Mary.__init__('lil')
              Mary.__init__('lil', last_name=None)
              # use the default value
              super().__init__(first_name)
              # no last_name passed to Parent
          Joe.__init__('lil')
              super().__init__(first_name)
              self.eye_color = 'blue'
          Blow.__init__('lil')
              super().__init__(
                  first_name=first_name,
                  last_name='blow'
              )
          # call the next parent of Mary
          Jane.__init__('lil', last_name='blow')
    

    which raises TypeError since the __init__ method of Jane does not have a parameter named last_name. Confused?

  • I add last_name to Jane

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

    the terminal is my friend, and 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: 'doe' != 'blow'
    

    for the expectation of mary.last_name because this happens when mary = src.family_ties.Mary() runs, if the parents of Mary are (Joe, Jane)

    mary = src.family_ties.Mary()
           Mary.__init__(
               first_name='mary',
               last_name=None,
           )
               super().__init__(first_name)
           Joe.__init__('mary')
               super().__init__(first_name)
               self.eye_color = 'blue'
           Blow.__init__('mary')
               super().__init__(
                   first_name=first_name,
                   last_name='blow'
               )
           # call the next parent of Mary
           Jane.__init__('mary', last_name='blow')
               super().__init__('mary')
               self.eye_color = 'green'
           Doe.__init__('mary')
               super().__init__('mary')
           Person.__init__('mary')
               Person.__init__('mary', last_name='doe')
               self.first_name = 'mary'
               self.last_name = 'doe' # use the default value
    
  • I change the expectation of the assertion for mary.last_name in test_classes_w_multiple_parents

    67        # mary = src.family_ties.Jane('mary')
    68        mary = src.family_ties.Mary()
    69        self.assertEqual(mary.first_name, 'mary')
    70        # self.assertEqual(mary.last_name, mary.first_name)
    71        self.assertEqual(mary.last_name, jane.last_name)
    72        # self.assertEqual(mary.last_name, joe.last_name)
    73        # self.assertEqual(mary.eye_color, jane.eye_color)
    74        self.assertEqual(mary.eye_color, joe.eye_color)
    75        # self.assertNotIsSubclass(
    76        self.assertIsSubclass(
    77            src.family_ties.Mary, src.family_ties.Jane
    78        )
    79        self.assertIsSubclass(
    80            src.family_ties.Mary, src.family_ties.Joe
    81        )
    

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

    lil = src.family_ties.Lil()
          Lil.__init__()
              super().__init__('lil')
          Mary.__init__(
              first_name='lil',
              last_name=None,
          )
              super().__init__(first_name)
          Joe.__init__('lil')
              super().__init__(first_name)
              self.eye_color = 'blue'
          Blow.__init__('lil')
              super().__init__(
                  first_name=first_name,
                  last_name='blow'
              )
          # call the next parent of Mary
          Jane.__init__(
              first_name='lil',
              last_name='blow',
          )
              super().__init__('lil')
              # last_name does not get passed to parent
              self.eye_color = 'green'
          Doe.__init__('lil')
              super().__init__('lil')
          # call the next parent of Lil
          John.__init__('lil')
              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 remove the commented lines from family_ties.py

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

  • I add an assertion for lil.eye_color to test if instances of Lil inherit eye_color from Jane or Joe, in test_classes_w_multiple_parents in test_family_ties.py

     90        lil = src.family_ties.Lil()
     91        self.assertEqual(lil.first_name, 'lil')
     92        self.assertEqual(lil.last_name, john.last_name)
     93        self.assertEqual(lil.eye_color, jane.eye_color)
     94        self.assertIsSubclass(
     95            src.family_ties.Lil, src.family_ties.John
     96        )
     97        self.assertIsSubclass(
     98            src.family_ties.Lil, src.family_ties.Mary
     99        )
    100
    101
    102# Exceptions seen
    

    the terminal is my friend, and shows AssertionError

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

     90        lil = src.family_ties.Lil()
     91        self.assertEqual(lil.first_name, 'lil')
     92        self.assertEqual(lil.last_name, john.last_name)
     93        # self.assertEqual(lil.eye_color, jane.eye_color)
     94        self.assertEqual(lil.eye_color, mary.eye_color)
     95        self.assertIsSubclass(
     96            src.family_ties.Lil, src.family_ties.John
     97        )
     98        self.assertIsSubclass(
     99            src.family_ties.Lil, src.family_ties.Mary
    100        )
    101
    102
    103# Exceptions seen
    

  • I add an assertion for joe.eye_color that will fail

    51    def test_classes_w_multiple_parents(self):
    52        joe = src.family_ties.Joe()
    53        self.assertEqual(joe.first_name, 'joe')
    54        self.assertEqual(joe.last_name, 'blow')
    55        self.assertEqual(joe.eye_color, '')
    56        self.assertIsSubclass(
    57            src.family_ties.Joe, src.family_ties.Blow
    58        )
    

    the terminal is my friend, and shows AssertionError

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

    51    def test_classes_w_multiple_parents(self):
    52        joe = src.family_ties.Joe()
    53        self.assertEqual(joe.first_name, 'joe')
    54        self.assertEqual(joe.last_name, 'blow')
    55        # self.assertEqual(joe.eye_color, '')
    56        self.assertEqual(joe.eye_color, 'blue')
    57        self.assertIsSubclass(
    58            src.family_ties.Joe, src.family_ties.Blow
    59        )
    

    the test passes.

  • I add an assertion for john.eye_color

    85        john = src.family_ties.John()
    86        self.assertEqual(john.first_name, 'john')
    87        self.assertEqual(john.last_name, 'smith')
    88        self.assertEqual(john.eye_color, '')
    89        self.assertIsSubclass(
    90            src.family_ties.John, src.family_ties.Smith
    91        )
    

    the terminal is my friend, and shows AttributeError

    AttributeError: 'John' object has no attribute 'eye_color'
    
  • I add the eye_color attribute to Smith in family_ties.py

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

    the terminal is my friend, and shows AssertionError

    AssertionError: 'brown' != ''
    
  • I change the expectation of the assertion for john.eye_color in test_classes_w_multiple_parents in test_family_ties.py

    85        john = src.family_ties.John()
    86        self.assertEqual(john.first_name, 'john')
    87        self.assertEqual(john.last_name, 'smith')
    88        # self.assertEqual(john.eye_color, '')
    89        self.assertEqual(john.eye_color, 'brown')
    90        self.assertIsSubclass(
    91            src.family_ties.John, src.family_ties.Smith
    92        )
    

    the test passes.

  • I remove the commented lines

     51    def test_classes_w_multiple_parents(self):
     52        joe = src.family_ties.Joe()
     53        self.assertEqual(joe.first_name, 'joe')
     54        self.assertEqual(joe.last_name, 'blow')
     55        self.assertEqual(joe.eye_color, 'blue')
     56        self.assertIsSubclass(
     57            src.family_ties.Joe, src.family_ties.Blow
     58        )
     59
     60        jane = src.family_ties.Jane()
     61        self.assertEqual(jane.first_name, 'jane')
     62        self.assertEqual(jane.last_name, 'doe')
     63        self.assertEqual(jane.eye_color, 'green')
     64        self.assertIsSubclass(
     65            src.family_ties.Jane, src.family_ties.Doe
     66        )
     67
     68        mary = src.family_ties.Mary()
     69        self.assertEqual(mary.first_name, 'mary')
     70        self.assertEqual(mary.last_name, jane.last_name)
     71        self.assertEqual(mary.eye_color, joe.eye_color)
     72        self.assertIsSubclass(
     73            src.family_ties.Mary, src.family_ties.Jane
     74        )
     75        self.assertIsSubclass(
     76            src.family_ties.Mary, src.family_ties.Joe
     77        )
     78
     79        john = src.family_ties.John()
     80        self.assertEqual(john.first_name, 'john')
     81        self.assertEqual(john.last_name, 'smith')
     82        self.assertEqual(john.eye_color, 'brown')
     83        self.assertIsSubclass(
     84            src.family_ties.John, src.family_ties.Smith
     85        )
     86
     87        lil = src.family_ties.Lil()
     88        self.assertEqual(lil.first_name, 'lil')
     89        self.assertEqual(lil.last_name, john.last_name)
     90        self.assertEqual(lil.eye_color, mary.eye_color)
     91        self.assertIsSubclass(
     92            src.family_ties.Lil, src.family_ties.John
     93        )
     94        self.assertIsSubclass(
     95            src.family_ties.Lil, src.family_ties.Mary
     96        )
     97
     98
     99# Exceptions seen
    100# AssertionError
    101# NameError
    102# AttributeError
    103# ModuleNotFoundError
    104# TypeError
    
  • 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 in family_ties.py and it would not have given me the chance to practice making classes with multiple parents and seeing how Python resolves them.

joe = src.family_ties.Joe()
joe = src.person.Person('joe', last_name='blow')
joe.eye_color = 'blue'
jane = src.family_ties.Jane()
jane = src.person.Person('jane')
jane.eye_color = 'green'
mary = src.family_ties.Mary()
mary = src.person.Person('mary', joe.last_name)
mary.eye_color = joe.eye_color
john = src.family_ties.John()
john = src.person.Person('john', 'smith')
john.eye_color = 'brown'
lil = src.family_ties.Lil()
lil = src.person.Person('lil', john.last_name)
lil.eye_color = mary.eye_color

which would have just been Python making these calls to make instances (copies) 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
a_name.eye_color = color

I can make classes with multiple parents


review

I can make a class with


close the project

  • I close test_family_ties.py and family_ties.py

  • I click in the terminal where the tests are running

  • I 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?

Would you like to use class attributes with the ‘functions’ project?


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.