family ties

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

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

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

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


preview

These are the tests I have by the end of the chapter

 1import unittest
 2import src.classes
 3import src.person
 4
 5
 6class TestClasses(unittest.TestCase):
 7
 8    def test_making_a_class_w_pass(self):
 9        self.assertIsInstance(src.classes.WPass(), object)
10
11    def test_making_a_class_w_parentheses(self):
12        self.assertIsInstance(src.classes.WParentheses(), object)
13
14    def test_making_a_class_w_object(self):
15        self.assertIsInstance(src.classes.WObject(), object)
16
17    def test_attributes_and_methods_of_objects(self):
18        self.assertEqual(
19            dir(object),
20            [
21                '__class__',
22                '__delattr__',
23                '__dir__',
24                '__doc__',
25                '__eq__',
26                '__format__',
27                '__ge__',
28                '__getattribute__',
29                '__getstate__',
30                '__gt__',
31                '__hash__',
32                '__init__',
33                '__init_subclass__',
34                '__le__',
35                '__lt__',
36                '__ne__',
37                '__new__',
38                '__reduce__',
39                '__reduce_ex__',
40                '__repr__',
41                '__setattr__',
42                '__sizeof__',
43                '__str__',
44                '__subclasshook__'
45            ]
46        )
47
48    def test_making_classes_w_inheritance(self):
49        self.assertIsInstance(
50            src.classes.Doe('doe'),
51            src.person.Person
52        )
53        self.assertEqual(
54            dir(src.classes.Doe),
55            dir(src.person.Person)
56        )
57
58    def test_family_ties(self):
59        doe = src.classes.Doe('doe')
60        jane = src.classes.Doe('jane')
61        john = src.classes.Doe('john')
62        mary = src.classes.Smith('mary')
63        joe = src.classes.Blow('joe')
64        baby = src.classes.Baby('baby')
65        lil = src.classes.Lil('lil')
66
67        self.assertEqual(doe.last_name, 'doe')
68        self.assertEqual(jane.last_name, 'doe')
69        self.assertEqual(john.last_name, 'doe')
70        self.assertEqual(mary.last_name, 'smith')
71        self.assertEqual(joe.last_name, 'blow')
72        self.assertEqual(baby.last_name, 'blow')
73        self.assertEqual(lil.last_name, 'doe')
74
75
76# Exceptions seen
77# AssertionError

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 activate the virtual environment

    source .venv/bin/activate
    

    Attention

    on Windows without Windows Subsystem for Linux use .venv/bin/activate.ps1 NOT source .venv/bin/activate

    .venv/scripts/activate.ps1
    

    the terminal shows

    (.venv) .../pumping_python/person
    
  • I use pytest-watch to run the tests

    pytest-watch
    

    the terminal shows

    rootdir: .../pumping_python/person
    collected 2 items
    
    tests/test_person.py ..                                             [100%]
    
    ============================ 2 passed in X.YZs =============================
    
  • I hold ctrl on the keyboard and click on tests/test_person.py to open it in the editor

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

  • I make another file in the src folder named classes.py


test_making_a_class_w_pass


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


RED: make it fail


  • I add an import statement for the classes module

    1import unittest
    2import src.classes
    3
    4
    5class TestClasses(unittest.TestCase):
    
  • I change test_failure to test_making_a_class_w_pass

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

    the terminal shows AttributeError

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

    there is no definition for WPass in classes.py


GREEN: make it pass


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

  • then I add a class definition to classes.py

    1class WPass:
    2
    3    pass
    

    the test passes

pass is a placeholder, it makes sure I am following Python rules and I can make a class with pass


test_making_a_class_w_parentheses


I can also make a class with parentheses.


RED: make it red


I add another test in test_classes.py

 7    def test_making_a_class_w_pass(self):
 8        self.assertIsInstance(src.classes.WPass(), object)
 9
10    def test_making_a_class_w_parentheses(self):
11        self.assertIsInstance(src.classes.WParentheses(), object)
12
13
14# Exceptions seen

the terminal shows AttributeError

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

GREEN: make it pass


I add a class definition like WPass to classes.py

1class WPass:
2
3    pass
4
5
6class WParentheses:
7
8    pass

the test passes


REFACTOR: make it better


  • I add parentheses to the definition

    6class WParentheses():
    7
    8    pass
    

    the terminal shows all tests are still passing.

pass is a placeholder, it makes sure I am following Python rules, I can make a class with


test_making_a_class_w_object


RED: make it fail


I add another test to TestClasses in test_classes.py

 7    def test_making_a_class_w_parentheses(self):
 8        self.assertIsInstance(src.classes.WParentheses(), object)
 9
10    def test_making_a_class_w_object(self):
11        self.assertIsInstance(src.classes.WObject(), object)
12
13
14# Exceptions seen

the terminal shows AttributeError

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

GREEN: make it pass


I add a class definition to classes.py

 6class WParentheses():
 7
 8    pass
 9
10
11class WObject():
12
13    pass

the test passes


REFACTOR: make it better


The last two tests pass because everything in Python is an object also known as a class. object is the mother class of all classes. I can use anything in the assertIsInstance method and the test would pass.

I use the examples to show different ways to make a class. I can also say who the parent of a class is when I define it. I add object to the definition

11class WObject(object):
12
13    pass

the test is still green. pass is a placeholder, it makes sure I am following Python rules, I can make a class with


test_attributes_and_methods_of_objects

I add a test to show the attributes and methods of object


RED: make it fail


I add a test to test_classes.py

13    def test_making_a_class_w_object(self):
14        self.assertIsInstance(src.classes.WObject(), object)
15
16    def test_attributes_and_methods_of_objects(self):
17        self.assertEqual(
18            dir(object),
19            []
20        )
21
22
23# Exceptions seen

the terminal shows AssertionError

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

GREEN: make it pass


I copy and paste the values from the terminal as the expectation and use the Find and Replace feature of the Integrated Development Environment (IDE) to remove the extra characters

16    def test_attributes_and_methods_of_objects(self):
17        self.assertEqual(
18            dir(object),
19            [
20                '__class__',
21                '__delattr__',
22                '__dir__',
23                '__doc__',
24                '__eq__',
25                '__format__',
26                '__ge__',
27                '__getattribute__',
28                '__getstate__',
29                '__gt__',
30                '__hash__',
31                '__init__',
32                '__init_subclass__',
33                '__le__',
34                '__lt__',
35                '__ne__',
36                '__new__',
37                '__reduce__',
38                '__reduce_ex__',
39                '__repr__',
40                '__setattr__',
41                '__sizeof__',
42                '__str__',
43                '__subclasshook__'
44            ]
45        )
46
47
48# Exceptions seen

and it passes. All classes automatically get these attributes, they inherit them


test_making_classes_w_inheritance


RED: make it fail


I add a new test

43                '__subclasshook__'
44            ]
45        )
46
47    def test_making_classes_w_inheritance(self):
48        self.assertIsInstance(
49            src.classes.Doe('doe'),
50            src.person.Person
51        )
52
53
54# Exceptions seen

the terminal shows AttributeError

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

GREEN: make it pass


  • I add a class definition to classes.py

    11class WObject(object):
    12
    13    pass
    14
    15
    16class Doe(object):
    17
    18    pass
    

    the terminal shows TypeError

    TypeError: Doe() takes no arguments
    
  • I add the __init__ method

    16class Doe(object):
    17
    18    def __init__(self):
    19        return None
    

    the terminal shows TypeError

    TypeError: Doe.__init__() takes 1 positional argument but 2 were given
    
  • I add a parameter to the method

    16class Doe(object):
    17
    18    def __init__(self, first_name):
    19        return None
    

    the terminal shows AssertionError

    AssertionError: <src.classes.Doe object at 0xffff01a2bc34> is not an instance of <class 'src.person.Person'>
    
  • I add an import statement at the top of classes.py

    1import src.person
    2
    3
    4class WPass:
    

    the terminal still shows AssertionError

  • I change the “parent” of Doe

    19class Doe(src.person.Person):
    20
    21    def __init__(self, first_name):
    22        return None
    

    the test passes


REFACTOR: make it better


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

    47    def test_making_classes_w_inheritance(self):
    48        self.assertIsInstance(
    49            src.classes.Doe('doe'),
    50            src.person.Person
    51        )
    52        self.assertEqual(
    53            dir(src.classes.Doe),
    54            []
    55        )
    56
    57
    58# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: Lists differ: ['__class__', '__delattr__', '__dict__', '[377 chars]llo'] != []
    
  • I change the expectation

    47    def test_making_classes_w_inheritance(self):
    48        self.assertIsInstance(
    49            src.classes.Doe('doe'),
    50            src.person.Person
    51        )
    52        self.assertEqual(
    53            dir(src.classes.Doe),
    54            dir(src.person.Person)
    55        )
    56
    57
    58# Exceptions seen
    

    the test passes. I do not need to add an import statement because classes.py imports src.person and I import src.classes at the beginning of test_person.py

  • I add the import statement to be clearer

    1import unittest
    2import src.classes
    3import src.person
    4
    5
    6class TestClasses(unittest.TestCase):
    

    the test is still green

  • I can remove the __init__ method from the Doe class

    19class Doe(src.person.Person): pass
    

    the test is still green


test_family_ties


RED: make it fail


  • I add a new test for Inheritance

    53        self.assertEqual(
    54            dir(src.classes.Doe),
    55            dir(src.person.Person)
    56        )
    57
    58    def test_family_ties(self):
    59        doe = src.classes.Doe('doe')
    60        jane = src.classes.Doe('jane')
    61        john = src.classes.Doe('john')
    62
    63
    64# Exceptions seen
    
  • I add an assertion for the last name of doe

    61        john = src.classes.Doe('john')
    62
    63        self.assertEqual(doe.last_name, '')
    64
    65
    66# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: 'doe' != ''
    

GREEN: make it pass


I change the expectation

63          self.assertEqual(doe.last_name, 'doe')

REFACTOR: make it better


  • I add another assertion

    61        john = src.classes.Doe('john')
    62
    63        self.assertEqual(doe.last_name, 'doe')
    64        self.assertEqual(jane.last_name, '')
    65
    66
    67# Exceptions seen
    

    the terminal shows AssertionError

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

    64        self.assertEqual(jane.last_name, 'doe')
    

    the test passes

  • I add one more assertion

    64        self.assertEqual(jane.last_name, 'doe')
    65        self.assertEqual(john.last_name, '')
    66
    67
    68# Exceptions seen
    

    the terminal shows AssertionError

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

    65        self.assertEqual(john.last_name, 'doe')
    

    the test passes. All 3 people made with the Doe class have the same last name, they are related.

  • I add a person from another family

    58    def test_family_ties(self):
    59        doe = src.classes.Doe('doe')
    60        jane = src.classes.Doe('jane')
    61        john = src.classes.Doe('john')
    62        mary = src.classes.Smith('mary')
    63
    64        self.assertEqual(doe.last_name, 'doe')
    

    the terminal shows AttributeError

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

    19class Doe(src.person.Person): pass
    20class Smith(src.person.Person): pass
    

    the test passes

  • I add an assertion for the last_name of mary

    66        self.assertEqual(john.last_name, 'doe')
    67        self.assertEqual(mary.last_name, '')
    68
    69
    70# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: 'doe' != ''
    
  • mary should have a last name of smith not doe. I change the expectation

    67        self.assertEqual(mary.last_name, 'smith')
    

    the terminal shows AssertionError

    AssertionError: 'doe' != 'smith'
    
  • I add a value for last_name to the Smith class in classes.py

    21class Smith(src.person.Person):
    22
    23    def __init__(self, first_name, last_name='smith'):
    24        pass
    

    the terminal shows AttributeError

    AttributeError: 'Smith' object has no attribute 'last_name'
    
  • I need to add the class attributes. I can do that by calling the __init__ method of the Person class. Python has a way for me to do that, I add it

    21class Smith(src.person.Person):
    22
    23    def __init__(self, first_name, last_name='smith'):
    24        super().__init__(first_name, last_name)
    

    the test passes.

    the super built-in function calls the __init__ method of the parent class with the values I pass in parentheses.

    In this case it calls the Person class with values for first_name and last_name

  • I add another person to test_classes.py

    61        john = src.classes.Doe('john')
    62        mary = src.classes.Smith('mary')
    63        joe = src.classes.Blow('joe')
    64
    65        self.assertEqual(doe.last_name, 'doe')
    

    the terminal shows AttributeError

    AttributeError: module 'src.classes' has no attribute 'Blow'
    
  • I add the class to classes.py

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

    the test passes

  • I add an assertion for the last name of joe

    67        self.assertEqual(john.last_name, 'doe')
    68        self.assertEqual(mary.last_name, 'smith')
    69        self.assertEqual(joe.last_name, 'blow')
    70
    71
    72# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: 'doe' != 'blow'
    
  • I add the __init__ method to the class

    26class Blow(src.person.Person):
    27
    28    def __init__(self, first_name, last_name='blow'):
    29        pass
    

    the terminal shows AttributeError

    AttributeError: 'Blow' object has no attribute 'last_name'
    
  • I use the super built-in function

    28class Blow(src.person.Person):
    29
    30    def __init__(self, first_name, last_name='blow'):
    31        super().__init__(first_name, last_name)
    

    the test passes

  • I add a new person who is a child of jane named baby in test_classes.py

    63        joe = src.classes.Blow('joe')
    64        baby = src.classes.Baby('baby')
    65
    66        self.assertEqual(doe.last_name, 'doe')
    

    the terminal shows AttributeError

    AttributeError: module 'src.classes' has no attribute 'Baby'
    
  • I add a class for baby in classes.py

    27class Blow(src.person.Person):
    28
    29    def __init__(self, first_name, last_name='blow'):
    30        super().__init__(first_name, last_name)
    31
    32
    33class Baby(Doe): pass
    

    the test passes

  • baby is also the child of joe. I add an assertion for the last name of baby in test_classes.py

    69        self.assertEqual(mary.last_name, 'smith')
    70        self.assertEqual(joe.last_name, 'blow')
    71        self.assertEqual(baby.last_name, 'blow')
    72
    73
    74# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: 'doe' != 'blow'
    
  • I add another parent to the Baby class in classes.py

    33class Baby(Blow, Doe): pass
    

    the test passes

  • I add another person, a child of john in test_classes.py

    63        joe = src.classes.Blow('joe')
    64        baby = src.classes.Baby('baby')
    65        lil = src.classes.Lil('lil')
    66
    67        self.assertEqual(doe.last_name, 'doe')
    

    the terminal shows AttributError

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

    33class Baby(Blow, Doe): pass
    34
    35
    36class Lil(Doe): pass
    

    the test passes

  • lil is also the child of mary. I add an assertion for the last name of lil

    71        self.assertEqual(joe.last_name, 'blow')
    72        self.assertEqual(baby.last_name, 'blow')
    73        self.assertEqual(lil.last_name, '')
    74
    75
    76# Exceptions seen
    

    the terminal shows AssertionError

    AssertionError: 'doe' != ''
    
  • I add another parent to show that lil is a child of Doe and Smith in classes.py

    37class Lil(Doe, Smith): pass
    

    the terminal shows AssertionError

    AssertionError: 'smith' != ''
    

    this is a problem. When I made Baby, it took the last name of the first parent, and when I try the same thing with Lil it has the last name of the second parent

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

    19class Doe(src.person.Person):
    20
    21    def __init__(self, first_name):
    22        super().__init__(first_name)
    23
    24
    25class Smith(src.person.Person):
    

    the terminal shows TypeError

    TypeError: Doe.__init__() takes 2 positional arguments but 3 were given
    

    because the Baby class passes a value for last_name

  • I add last_name to the call to __init__ method

    19class Doe(src.person.Person):
    20
    21    def __init__(self, first_name, last_name='doe'):
    22        super().__init__(first_name, last_name)
    

    the terminal shows AssertionError

    AssertionError: 'doe' != ''
    
  • I change the expectation in test_classes.py

    self.assertEqual(lil.last_name, 'doe')
    

    the test passes


review

I can make a class with


close the project

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

  • I click in the terminal and exit the tests with ctrl+c on the keyboard

  • I deactivate the virtual environment

    deactivate
    

    the terminal goes back to the command line, (.venv) is no longer on the left side

    .../pumping_python/person
    
  • I change directory to the parent of person

    cd ..
    

    the terminal shows

    .../pumping_python
    

    I am back in the pumping_python directory


code from the chapter

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


what is next?

you have gone through a lot of things and know

Would you like to test if bool_ is an int_?


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