teach_cs_toronto_edu_csc148h_notes_inheritance_inheritance_attributes_html
teach_cs_toronto_edu_csc148h_notes_inheritance_inheritance_attributes_html
1.4 Preconditions We decided earlier that the application would need to record an id and a name for all employees. Here’s how we document
that in the base class:[1]
1.5 Python Type Annotations
3. Object-Oriented Programming
3.8 Inheritance: Thoughts on Design Round the amount to the nearest cent.
"""
3.9 The object Class and Python raise NotImplementedError
Special Methods
def pay(self, date: str) -> None:
"""Pay this Employee on the given date and record the payment.
4. Abstract Data Types
(Assume this is called once per month.)
4.1 Introduction to Abstract Data """
Types payment = self.get_monthly_payment()
print(f'An employee was paid {payment} on {date}.')
4.2 Stacks and Queues
4.3 Exceptions
5.1 Introduction to Exceptions >>> # Assuming SalariedEmployee does not override Employee.__init__,
>>> # that method is called when we construct a SalariedEmployee.
5.2 General Rules for try-except
>>> fred = SalariedEmployee(99, 'Fred Flintstone')
5.3 Why Not Just Return a Special >>> # We can see that Employee.__init__ was called,
>>> # and the two instance attributes have been initialized.
Value? >>> fred.name
'Fred Flintstone'
5.4 Additional Clauses
>>> fred.id_
99
6. Linked Lists
6.1 Introduction to Linked Lists Just as with all other methods, for each subclass, we must decide whether the inherited implementation is suitable for our
6.2 Traversing Linked Lists class, or whether we want to override it. In this case, the inherited initializer is not suitable, because each subclass requires
that additional instance attributes be initialized: For each SalariedEmployee we need to keep track of the employee’s salary,
6.3 Linked List Mutation
and for each HourlyEmployee we need to keep track of their number of work hours per week and their hourly wage.
6.4 Linked Lists and Running Time
Certainly we could override and replace the inherited initializer, and in its body copy the code from Employee.__init__ :
7. Recursion
7.8 Branching recursion Since the inherited initializer does part of the work by initializing the attributes that all employees have in common, we can
instead use Employee.__init__ as a helper method. In other words, rather than override and replace this method, we will
8. Trees and Binary Search Trees override and extend it. As we saw briefly last week, we use the superclass name to access its method:[2]
8.1 Introduction to Trees
Representation Invariants:
9. Recursive Sorting Algorithms - self.salary >= 0
"""
9.1 Recursive Sorting Algorithms id_: int
name: str
9.2 Efficiency of Recursive Sorting salary: float
Algorithms
def __init__(self, id_: int, name: str, salary: float) -> None:
# Note that to call the superclass initializer, we need to use the
# full method name '__init__'. This is the only time you should write
# '__init__' explicitly.
Employee.__init__(self, id_, name)
self.salary = salary
class HourlyEmployee(Employee):
"""An employee whose pay is computed based on an hourly rate.
Attributes:
hourly_wage:
This employee's hourly rate of pay.
hours_per_month:
The number of hours this employee works each month.
Representation Invariants:
- self.hourly_wage >= 0
- self.hours_per_month >= 0
"""
id_: int
name: str
hourly_wage: float
hours_per_month: float
We can see that when we construct an instance of either subclass, both the common instance attributes ( name and id_ ) and
the subclass-specific attributes are initialized:
We have now completed the second version of the code . Download it so that you can experiment with it as you
continue reading.
The only reason that fred has a name attribute is because the SalariedEmployee initializer explicitly calls the Employee
initializer, which initializes this attribute. A superclass initializer is not called automatically when a subclass instance is created.
If we remove this call from our example, we see that the two attributes name and id_ are missing:
class SalariedEmployee(Employee):
def __init__(self, id_: int, name: str, salary: float) -> None:
# Superclass call commented out:
# Employee.__init__(self, id_, name)
self.salary = salary
Because abstract classes aren’t meant to be instantiated directly, their initializers are considered private, and so can be freely
overridden and have their signatures changed in each subclass. This offers flexibility in specifying how subclasses are created,
and in fact it is often the case that different subclasses of the same abstract class will have different initializer signatures.
However, subclass initializers should always call the initializer of their superclass!
It turns out that Python allows us to change the signature of any method we override, not just __init__ . However, as we’ll
discuss in the next section, in this course we’ll use inheritance to define interfaces that your subclasses should implement.
Because a function signature is a crucial part of its interface, you should not do this for uses of inheritance in this course.
[1] We put an underscore at the end of the attribute id_ in order to distinguish it from the built-in function id .
[2] Python has a much more powerful mechanism for accessing the superclass without naming it directly. It involves the built-
in super function, but this is beyond the scope of this course.
[3] In this course, we also include type annotations from the parent class. For a technical reason, the current version of
python_ta sometimes complains when these type annotations are missing.
Previous Next
3.5 Inheritance: Introduction and Methods 3.7 Inheritance: Tracing Initialization