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 object and customize it for what I need instead of making new objects 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 an object 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
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_an_object_w_inheritance(self):
8 person_object = 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_object
14 )
15
16 self.assertNotIsInstance(
17 doe_class, doe_class
18 )
19
20 self.assertIsSubclass(
21 doe_class, person_object
22 )
23
24 self.assertIsInstance(
25 doe_instance, person_object
26 )
27
28 self.assertEqual(
29 dir(doe_class), dir(person_object)
30 )
31
32 def test_objects_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_objects_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
personfolderthe terminal shows I am in the
personfolder.../pumping_python/personI make a new file in the
testsfolder namedtest_family_ties.pyI open
test_family_ties.pyI add the first failing test to
test_family_ties.py1import 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
the terminal goes back to the command line.
I use
pytest-watcherto run the teststhe 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.pyI change True to False in the assertion
the test passes.
test_making_an_object_w_inheritance
I know from test_making_an_object_w_object that I can make objects with inheritance by stating the parent object 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_an_object_w_inheritance with an assertion
the terminal is my friend, and shows NameError
NameError: name 'src' is not definedbecause
srcis not defined in this file.I add NameError to the list of Exceptions seen
GREEN: make it pass
I add an import statement for the
family_tiesmodule at the top of the file1import src.family_ties 2import unittest 3 4 5class TestFamilyTies(unittest.TestCase):import src.family_tiesbrings in an object for thefamily_ties.pymodule from thesrcfolder so I can use it intest_family_ties.pythe terminal is my friend, and shows AttributeError
AttributeError: module 'src.family_ties' has no attribute 'Doe'because there is no definition for
Doeinfamily_ties.py
I add AttributeError to the list of Exceptions seen
I open
family_ties/__init__.pyfrom thesrcfolderI add an object definition definition to
family_ties.py1class Doe(object): passthe terminal is my friend, and shows AssertionError
AssertionError: <class 'src.family_ties.Doe'> is not an instance of <class 'src.person.Person'>because
Doeis not an instance (a copy) ofPerson.I change the assertion in test_making_an_object_w_inheritance in
test_family_ties.pythe test passes.
Doeis NOT an instance of thePersonobject, they are siblings - both children of object.
REFACTOR: make it better
I add a call to assertIsSubclass
the terminal is my friend, and shows AssertionError
AssertionError: <class 'src.family_ties.Doe'> is not a subclass of <class 'src.person.Person'>because
Doeis not a child ofPerson, yet.I change the parent of
DoefromobjecttoPersoninfamily_ties.py1# class Doe(object): pass 2class Doe(person.Person): passthe terminal is my friend, and shows NameError
NameError: name 'person' is not definedbecause there is no definition for
personinfamily_ties.pyI add an import statement at the top of
family_ties.py1import person 2 3 4# class Doe(object): pass 5class Doe(person.Person): passthe terminal is my friend, and shows ModuleNotFoundError
E ModuleNotFoundError: No module named 'person'because Python cannot find
src/person/__init__.pyin the main project folder (the parent ofsrcandtests) 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.pyI add the path of
src/person/__init__.pyfrom the main project folder (the parent ofsrcandtests) to the import statement, infamily_ties.py1# import person 2import src.person 3 4 5# class Doe(object): pass 6class Doe(person.Person): passthe terminal is my friend, it goes back to NameError
NameError: name 'person' is not definedbecause there is no definition for
personin this file.I add
src.to the parent ofDoeimport src.personbrings in an object for thesrc/person/__init__.pymodule from thesrcfolder so I can use it infamily_ties.py.I have to use
src.person.Personinfamily_ties.pybecause I am testing from the root folder of the project (the parent folder ofsrcandtests).The test needs to know where
src/person/__init__.pyis in relation to where I ran the tests from.This is a problem because if
family_ties.pyis run from insidesrcthe import statement will not be able to findsrc.personfrom insidesrc. Same thing if I run the tests from insidetests(a problem for another time).The test passes because
Doeis now a child (subclass) ofPerson.
more about instances vs subobjects
I add a call to assertIsInstance to show that
src.family_ties.Doeis not an instancethe terminal shows AssertionError
AssertionError: <class 'src.family_ties.Doe'> is not an instance of <class 'src.family_ties.Doe'>I change assertIsInstance to assertNotIsInstance
I add a variable
I use the variable to remove repetition of
src.family_ties.Doefrom the testthe 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
Doethe terminal is my friend, and shows TypeError
TypeError: Person.__init__() missing 1 required positional argument: 'first_name'I called
Doeto make an instance. How didPerson.__init__get called?Here is what is happens when
src.class.Doe()runssrc.family_ties.Doe() Doe # has no __init__ # call the parent of Doe (Person) Person.__init__()which raises TypeError since the __init__ method of the
Personobject takes one required argument forfirst_name.I add TypeError to the list of Exceptions seen
how to call the parent from the child
I add the super built-in function to
Doeto call the parent __init__ method (Parent.__init__) directly fromDoe, infamily_ties.pythe super built-in function calls the __init__ method of the parent object
super()is the parent - “super class” for parent, “subclass” for childsuperisPersonin this casesuper().__init__()isPerson.__init__()in this casethe terminal still shows TypeError
TypeError: Person.__init__() missing 1 required positional argument: 'first_name'because this is what happens now when
src.class.Doe()runssrc.family_ties.Doe() Doe.__init__() super().__init__() # super is the parent (Person) Person.__init__()which raises TypeError since the __init__ method of the
Personobject takes one required argument forfirst_name.
I add a value for
first_nameto the call tosrc.family_ties.Doe()in test_making_an_object_w_inheritance intest_family_ties.pythe terminal is my friend, and shows TypeError
TypeError: Doe.__init__() takes 1 positional argument but 2 were givenbecause this happens when
src.family_ties.Doe('the_first')runssrc.family_ties.Doe('the_first') Doe.__init__('the_first')which raises TypeError since the definition for the __init__ method of
Doeonly takes one positional argument (self) and it was called with two (selfand'the_first')selfis the instance of the class.
I add a parameter for
first_nameto the __init__ method ofDoeinfamily_ties.pythe 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')runssrc.family_ties.Doe('the_first') Doe.__init__('the_first') super().__init__() # call the parent Person.__init__()which raises TypeError since the definition for the __init__ method of
Personrequires a positional argument forfirst_nameand it was called withselfselfis the instance of the class.
I add the required parameter to
super().__init__()inDoethe test passes because
an instance (a copy) of
Doeis an an instance (a copy) ofPersonPersonis the parent ofDoethe test shows that this happens when
src.family_ties.Doe('the_first')runssrc.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
Doeobjectthe 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
Personobjectthe test passes because
Doehas the same attributes and methods asPersonbecauseDoeis a child ofParent.I add a variable
I use the variable to remove repetition of
src.person.Personfrom the teststill green.
I add a variable for
src.family_ties.Doe('the_first')I use the new variable
green, because
doe_class()andsrc.objects.Doe()are the same sincedoe_class = src.objects.Doe.I remove the commented lines
I remove the commented lines from
family_ties.py1import src.person 2 3 4class Doe(src.person.Person): 5 6 def __init__(self, first_name): 7 super().__init__(first_name)
test_objects_w_one_parent
I want to test how the attributes of objects are set if they have only one parent (super object).
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.pythe terminal is my friend, and shows AssertionError
AssertionError: 'doe' != ''
GREEN: make it pass
I change the expectation
32 def test_objects_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
the terminal is my friend, and shows AttributeError
AttributeError: module 'src.family_ties' has no attribute 'Blow'because there is no definition for
Blowinfamily_ties.pyI add a new object definition to
family_ties.pythe terminal is my friend, and shows AssertionError
AssertionError: 'doe' != 'blow'because this happens when
joe = src.family_ties.Blow('joe')runsjoe = 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 valueI add an object attribute for
last_namein theBlowobject infamily_ties.pythe terminal does not feel like my friend, it still shows AssertionError because this happens when
joe = src.family_ties.Blow('joe')runsjoe = 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 valuethe value for
last_namedoes not get sent to the parent (Person.__init__)I add the __init__ method to customize the last name
the terminal is my friend, and shows TypeError
TypeError: Blow.__init__() takes 1 positional argument but 2 were givenbecause this happens when
joe = src.family_ties.Blow('joe')runsjoe = src.family_ties.Blow('joe') Blow.__init__('joe')which raises TypeError since the __init__ method of
Blowonly takes one positional argument (self) and it got called with two (selfandfirst_name).selfis the instance of the class.
I add
first_nameto the parentheses of the__init__method definitionthe test passes because this happens when
joe = src.family_ties.Blow('joe')runsjoe = src.family_ties.Blow('joe') Blow.__init__('joe') self.last_name = 'joe'I can make objects that are related and have their own defaults. In this test
the
Doeobject has a default value for thelast_nameattribute that is the default value for thelast_nameattribute ofPersonthe
Blowobject has a different default value for the value of thelast_nameattributeDoeandBloware children (subobjects) ofPerson
I remove the commented lines
In this case there is a simpler way to make
joeanddoe. I could pass the values to thePersonobject directly, since all theBlowobject does is customize thelast_nameattribute, there is nothing special about it or theDoeobject. I add an assertion to test_objects_w_one_parent intest_family_ties.pythe terminal is my friend, and shows AssertionError
AssertionError: 'doe' != 'blow'I add
last_name='blow'to the callthe test passes. I can make an instance and change the values of its attributes without making a new object.
I add an assertion for
janethe terminal is my friend, and shows AssertionError
AssertionError: 'doe' != 'blow'I change the expectation
the test passes.
I add an assertion for
johnthe terminal shows AttributeError
AttributeError: module 'src.family_ties' has no attribute 'Smith'I add an object definition for
Smithtofamily_ties.pythe terminal is my friend, and shows AssertionError
AssertionError: 'doe' != 'smith'because this happens when
john = src.family_ties.Smith('john')runsjohn = 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 valueI add the __init__ method to
Smith16# class Smith(src.person.Person): pass 17class Smith(src.person.Person): 18 19 def __init__(self): 20 self.last_name = 'smith'TypeError: Smith.__init__() takes 1 positional argument but 2 were givenbecause this happens when
john = src.family_ties.Smith('john')runsjohn = src.family_ties.Smith('john') Smith.__init__('john')which raises TypeError since the definition for the __init__ method of
Smithtakes only one positional argument (self) and it got called with two (selfand'john')selfis the instance of the class.
I add
first_nameto the parentheses of the __init__ methodthe test passes because this happens when
john = src.family_ties.Smith('john')runsjohn = src.family_ties.Smith('john') Smith.__init__('john') self.last_name = 'smith'I remove the commented lines
the test passes.
I add another assertion to test_objects_w_one_parent in
test_family_ties.pythe terminal is my friend, and shows AssertionError
AssertionError: 'smith' != 'doe'I change the expectation
the test passes.
I remove the commented lines
From the object definition of
Doe, this happens when an instance (a copy) ofDoeis madesrc.family_ties.Doe('first_name') Doe.__init__('first_name') super().__init__(first_name) Person.__init__('first_name') Person.__init__('first_name', last_name='doe') self.last_name = 'doe' # use the default valuebecause a method uses the default value for the parameter because it is called without the parameter.
From the object definitions of
SmithandBlowthis happens when an instance (a copy) ofSmithorBlowis madesrc.family_ties.ClassName('first_name') ClassName.__init__('first_name') self.last_name = 'last_name'where
ClassNameisSmithorBlowFrom the object definition of Person, this happens when an instance (a copy) of the
Personobject is madesrc.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
it will use the default value for
last_nameif no value is given because a function uses the default value for the parameter because it is called without the parameter.
test_objects_w_multiple_parents
Can an object have more than one parent? How are the attributes set if they have more than one parent (super object)?
RED: make it fail
I go back to the terminal where the tests are running
I add a test with an assertion for
janethe terminal is my friend, and shows AttributeError
AttributeError: module 'src.family_ties' has no attribute 'Jane'
GREEN: make it pass
I add an object definition for
Janetofamily_ties.pyTypeError: Person.__init__() missing 1 required positional argument: 'first_name'because this happens when
jane = src.family_ties.Jane()runsjane = src.family_ties.Jane() Jane # has no __init__ # call the parent of Jane (Person) Person.__init__()which raises TypeError since the __init__ method of
Personrequires one positional argument (first_name) and it got called with zeroI add the __init__ method to the definition of
Janethe terminal is my friend, and shows AttributeError
AttributeError: 'Jane' object has no attribute 'first_name'I add a value for
first_nameto the definition
the test passes.
REFACTOR: make it better
I add an assertion for the value of the
last_nameattribute ofjaneto test_objects_w_multiple_parents intest_family_ties.pythe 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_nametoJaneinfamily_ties.pythe test passes. This is a repetition because
the default value for
PersonisdoeJaneis a child (subclass) ofPerson
I add an assertion to test_objects_w_multiple_parents to make sure
Janeis aDoe, intest_family_ties.pythe 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
Janeinfamily_ties.pythe test passes.
I add a call to the super built-in function to use to remove the repetition of
last_nameTypeError: Doe.__init__() missing 1 required positional argument: 'first_name'because this happens when
jane = src.family_ties.Jane()runsjane = src.family_ties.Jane() Jane.__init__() super().__init__() Doe.__init__()which raises TypeError since the __init__ method of
Doerequires two positional arguments (selfandfirst_name) and it got called with one (self)I add
janeas the value forfirst_namein the call to the parentthe test is green again because this happens when
jane = src.family_ties.Jane()runsjane = 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 valuea method uses the default value for the parameter because it is called without the parameter.
I add an assertion for
mary, another instance ofJaneto test_objects_w_multiple_parents intest_family_ties.pythe terminal is my friend, and shows TypeError
TypeError: Jane.__init__() takes 1 positional argument but 2 were givenbecause this happens when
mary = src.family_ties.Jane('mary')runsmary = 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 (selfand'mary')I add
first_nameto the parentheses for the __init__ method ofJaneinfamily_ties.pythe terminal is my friend, and shows TypeError
TypeError: Jane.__init__() missing 1 required positional argument: 'first_name'I broke the assertion for
janebecause this happens whenjane = src.family_ties.Jane()runsjane = src.family_ties.Jane() Jane.__init__()which raises TypeError since the __init__ method takes two required positional arguments (
selfandfirst_name) and the call only sends one (self).I add a default value to make
first_nameoptionalthe terminal is my friend, and shows AssertionError
AssertionError: 'jane' != 'mary'because this happens when
mary = src.family_ties.Jane('mary')runsmary = 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 thefirst_nameparameter.I change the call to the super built-in function to use the name instead of a fixed value
the test passes.
I remove the commented lines and
return NoneI add an assertion that will fail, for the last name of
maryin test_objects_w_multiple_parents intest_family_ties.pythe terminal is my friend, and shows AssertionError
AssertionError: 'doe' != 'mary'I change the expectation to match reality
the test passes because
maryandjaneare instances ofJane
I add an assertion for
joethe terminal is my friend, and shows AttributeError
AttributeError: module 'src.family_ties' has no attribute 'Joe'. Did you mean: 'Doe'?I add an object definition for the
Joeobject tofamily_ties.pythe 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()runsjoe = src.family_ties.Joe() Person.__init__()which raises TypeError since the __init__ method takes two required positional arguments (
selfandfirst_name) and it was called with one (self)selfis the instance of the class
I add the __init__ method to
Joethe terminal is my friend, and shows AttributeError
AttributeError: 'Joe' object has no attribute 'first_name'I add
self.first_nametoJoewith a valuethe test passes.
I add an assertion to test_objects_w_multiple_parents to make sure that
joeis aBlow, intest_family_ties.pythe terminal is my friend, and shows AttributeError
AttributeError: 'Joe' object has no attribute 'last_name'. Did you mean: 'first_name'?I add
last_nameto the __init__ method ofJoeinfamily_ties.pythe test passes. I cheated, which means I need a better test.
I add assertIsSubclass to test_objects_w_multiple_parents to make sure
Joeis a child (subclass) ofBlow, intest_family_ties.pythe 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
JoetoBlowinfamily_ties.pythe test passes.
I no longer need
self.last_name = 'blow'because it is a repetition. I add a call to the super built-in functionthe 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()runsjoe = 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_nameattribute inBlow.I add
self.first_nametoBlowthe test passes because this happens when
joe = src.family_ties.Joe()runsjoe = 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, fromJoe
I change
maryto be an instance ofMary, a child (subclass) ofJanethe terminal is my friend, and shows AttributeError
AttributeError: module 'src.family_ties' has no attribute 'Mary'I add an object definition for
Marytofamily_ties.pythe terminal is my friend, and shows AssertionError
AssertionError: 'jane' != 'mary'because this happens when
mary = src.family_ties.Mary()runsmary = 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 valueI add the __init__ method to
Marythe 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()runsmary = src.family_ties.Mary() Mary.__init__() self.first_name = 'mary'I add a value for
last_namethe test passes. This is a repetition because
Maryis aJaneJaneis aDoeDoeis aPersonthe default value for
last_nameinPersonis'doe'
I add a call to the super built-in function to remove the repetition
the test is still green because this happens when
mary = src.family_ties.Mary()runsmary = 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 valueI add a call to the assertNotIsSubobject method to test_objects_w_multiple_parents in
test_family_ties.pythe 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 assertIsSubobject method
the test passes.
what happens when a child has more than one parent?
I add an assertion to test if I can make
JoeandJaneboth be parents ofMary?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
Maryis not a child (subclass) ofJoe.I add
Joeas a parent ofMaryinfamily_ties.pythe terminal is my friend, and shows TypeError
TypeError: Joe.__init__() takes 1 positional argument but 2 werebecause this happens when
mary = src.family_ties.Mary()runsmary = 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')which raises TypeError since the __init__ method of
Joeonly takes one positional argument (self) and it got called with two (selfandmary)selfis the instance of the class.
I change the __init__ method of
Joeto take afirst_nameargumentthe 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 (selfandfirst_name) and it was called with one (self)I add a default value to make
first_nameoptionalthe terminal is my friend, and shows AssertionError
AssertionError: 'joe' != 'mary'for
mary.first_namebecause this happens whenmary = src.family_ties.Mary()runsmary = 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
superinstead of a fixed value inJoethe terminal shows AssertionError
AssertionError: 'blow' != 'doe'I change the expectation of the assertion in test_objects_w_multiple_parents for the last name of
maryintest_family_ties.pythe test passes.
I change the order of the parents of
Maryto see what it does to the value oflast_name, infamily_ties.pythe test is still green because this happens when
mary = src.family_ties.Mary()runsmary = 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
Janedid not get called even though it is a parent ofMaryI add an assertion to show this in test_objects_w_multiple_parents in
test_family_ties.pythe terminal is my friend, and shows AssertionError
AttributeError: 'Jane' object has no attribute 'eye_color'I add an object attribute to
Janeinfamily_ties.pythe test passes.
I add an assertion for the
eye_colorattribute ofMaryin test_objects_w_multiple_parents intest_family_ties.pythe terminal is my friend, and shows AttributeError
AttributeError: 'Mary' object has no attribute 'eye_color'because the __init__ method of
Janedid not get called.I change the order of the parents of
Maryfrom(Joe, Jane)back to(Jane, Joe)infamily_ties.pythe test passes because this happens when
mary = src.family_ties.Mary()runsmary = 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.
If the order is
(Jane, Joe)then instances ofMaryget theeye_colorattribute ofJane.If the order is
(Joe, Jane)then instances ofMarydo NOT get theeye_colorattribute since the __init__ method ofJanedoes not get called.
I change the order of the parents to
(Joe, Jane)againthe terminal is my friend, and shows AttributeError
AttributeError: 'Mary' object has no attribute 'eye_color'I add the
eye_colorobject attribute toJoethe terminal is my friend, and shows AssertionError
AssertionError: 'blue' != 'green'I change the expectation of the assertion for
mary.eye_colorin test_objects_w_multiple_parents intest_family_ties.pythe test passes because this happens when
mary = src.family_ties.Mary()runsmary = 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.
If the order is
(Jane, Joe)then instances ofMaryget theeye_colorattribute ofJane.If the order is
(Joe, Jane)thenthe __init__ method of
Janedoes not get called
I remove the commented lines from
JoeandMaryinfamily_ties.py
I add
johnto test_objects_w_multiple_parents intest_family_ties.pythe terminal is my friend, and shows AttributeError
AttributeError: module 'src.family_ties' has no attribute 'John'I add an object definition for
Johntofamily_ties.pythe 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()runsjohn = src.family_ties.John() Person.__init__()which raises TypeError since the __init__ method of
Persontakes two positional arguments (selfandfirst_name) and it got called with one (self)I add the __init__ method to
Johninfamily_ties.pythe test passes.
I add an assertion to make sure that
Johnis a child (subclass) ofSmiththe 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
JohntoSmithinfamily_ties.pythe test passes.
I add an assertion for the
last_nameattributethe terminal is my friend, and shows AttributeError
AttributeError: 'John' object has no attribute 'last_name'. Did you mean: 'first_name'?I add an object attribute for
last_nametoJohninfamily_ties.pythe test passes. This is a repetition because
I add a call to the super built-in function so instances of
Johncan inherit thelast_nameobject attributethe terminal is my friend, and shows AttributeError
AttributeError: 'John' object has no attribute 'first_name'. Did you mean: 'last_name'?I add the
first_nameattribute toSmiththe test passes because this happens when
john = src.family_ties.John()runsjohn = 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
I add
lil, an instance of a child (subclass) ofJohnto test_objects_w_multiple_parents intest_family_ties.pythe terminal is my friend, and shows AttributeError
AttributeError: module 'src.family_ties' has no attribute 'Lil'I add an object definition for
Liltofamily_ties.pythe test passes.
I add an assertion to test
JohnandMaryas parents ofLil?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
Maryas a parent toLilinfamily_ties.pythe test passes.
I add an assertion for the
first_nameattribute oflilintest_family_ties.pythe terminal shows AssertionError
AssertionError: 'john' != 'lil'because this happens when
lil = src.family_ties.Lil()runslil = 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_nameattribute intoLilinfamily_ties.pythe test passes.
I add an assertion for the
last_nameattribute oflil, in test_objects_w_multiple_parents intest_family_ties.pythe 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_nametoLilinfamily_ties.pythe test passes. This is a repetition, and a problem if the value of the
last_nameattribute of the parent changes.I add a call to the super built-in function to remove the repetition
the terminal is my friend, and shows TypeError
TypeError: John.__init__() takes 1 positional argument but 2 were givenbecause this happens when
lil = src.family_ties.Lil()runslil = src.family_ties.Lil() Lil.__init__() super().__init__('lil') John.__init__('lil')which raises TypeError since the __init__ method of
Johntakes one positional argument (self) and it was called with two (selfandlil)I change the __init__ method in
Johnto take in a parameter forfirst_namewith a default value to make it optionalthe terminal shows AssertionError
AssertionError: 'smith' != 'blow'because this happens when
lil = src.family_ties.Lil()runslil = 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_namethe __init__ method of
Marydid not get called.I add a call to the super built-in function in
Smiththe 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()runslil = 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' )which raises TypeError since the __init__ method of
Marywas called with a keyword argument and it only takesselfselfis the instance of the class.
I add
first_namewith a default value to the __init__ method ofMarythe 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_nameto the __init__ methodthe terminal shows SyntaxError
SyntaxError: parameter without a default follows parameter with a defaultbecause parameters without default values must come before parameters with default values.
I add a default value for
last_namethe test passes because this happens when
lil = src.family_ties.Lil()runslil = 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
Janedid not get called.I change the order of the parents of
Lilto(Mary, John)to see if the value will change tojohn.last_namethe test is still green because this happens when
lil = src.family_ties.Lil()runslil = 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
Janedid not get called.I add a call to the super built-in function in
Blowthe 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()runslil = 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
Janedoes not have a parameter namedlast_name. Confused?I add
last_nametoJanethe terminal is my friend, and shows SyntaxError
SyntaxError: parameter without a default follows parameter with a defaultbecause parameters without default values must come before parameters with default values.
I give
last_namea default valuethe terminal is my friend, and shows AssertionError
AssertionError: 'doe' != 'blow'for the expectation of
mary.last_namebecause this happens whenmary = src.family_ties.Mary()runs, if the parents ofMaryare(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 valueI change the expectation of the assertion for
mary.last_namein test_objects_w_multiple_parentsthe test passes because this happens when
lil = src.family_ties.Lil()runslil = 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
I add an assertion for
lil.eye_colorto test if instances ofLilinheriteye_colorfromJaneorJoe, in test_objects_w_multiple_parents intest_family_ties.pythe terminal is my friend, and shows AssertionError
AssertionError: 'blue' != 'green'I change the expectation of the assertion
I add an assertion for
joe.eye_colorthat will failthe terminal is my friend, and shows AssertionError
AssertionError: 'blue' != ''I change the expectation of the assertion
the test passes.
I add an assertion for
john.eye_colorthe terminal is my friend, and shows AttributeError
AttributeError: 'John' object has no attribute 'eye_color'I add the
eye_colorattribute toSmithinfamily_ties.pythe terminal is my friend, and shows AssertionError
AssertionError: 'brown' != ''I change the expectation of the assertion for
john.eye_colorin test_objects_w_multiple_parents intest_family_ties.pythe test passes.
I remove the commented lines
Note
All the instances could have been made with only the Person object because there was nothing unique about the objects I made in family_ties.py and it would not have given me the chance to practice making objects 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 object
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
review
I can make an object with
close the project
I close
test_family_ties.pyandfamily_ties.pyI 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
personthe terminal shows
.../pumping_pythonI am back in the
pumping_pythondirectory.
code from the chapter
what is next?
I know how to make a Python Test Driven Development environment manually.
how to use object attributes to remove repetition
Would you like to use object 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.