Protected methods
Methods have the same three levels of access as attributes. Most methods are public: they are what the class offers its clients. Classes often have helper methods too, small methods that the class's other methods call to do part of their work. In the module on encapsulation such helpers were private, with two underscores. A private method can't be called from a derived class, for the same reason that a private attribute can't be used there: Python renames it after the class it's written in. A helper that derived classes should be able to use is made protected, with one underscore.
Here a receipt keeps its prices in cents, as whole numbers, so that the sums are always exact. Its protected helper _euros turns cents into text. The derived class DiscountReceipt adds a discount and uses the same helper for its own lines:
class Receipt:
def __init__(self):
self._lines = []
def _euros(self, cents):
return f"{cents / 100:.2f} eur"
def add(self, product, cents):
self._lines.append((product, cents))
def total(self):
total = 0
for product, cents in self._lines:
total += cents
return total
def show(self):
for product, cents in self._lines:
print(product, self._euros(cents))
print("Total", self._euros(self.total()))
class DiscountReceipt(Receipt):
def __init__(self, percent):
super().__init__()
self._percent = percent
def show(self):
super().show()
discount = self.total() * self._percent // 100
print(f"Discount {self._percent}%", self._euros(discount))
print("To pay", self._euros(self.total() - discount))
receipt = DiscountReceipt(10)
receipt.add("Bread", 349)
receipt.add("Cheese", 651)
receipt.show()
Bread 3.49 eur
Cheese 6.51 eur
Total 10.00 eur
Discount 10% 1.00 eur
To pay 9.00 eurThe receipt's clients only need add, total and show. _euros is a tool for the receipt family, so it's protected. Had it been private, __euros, the call in DiscountReceipt would stop with an AttributeError, just as the private list of songs did. Notice also that DiscountReceipt stores its percentage in a protected attribute of its own, _percent, so that classes derived from it could use it too.
A protected method can be overridden like any other method. If a derived class wanted to show prices in another way, it could write its own _euros, and every line show prints would use it.
Choosing the level of access
- Public,
name: what clients use. Keep this small, and don't change it lightly, since other code depends on it. - Protected,
_name: data and tools for the class and its derived classes. - Private,
__name: details that only this one class handles, which not even a derived class should touch.
When you're unsure, start with less access. Opening something up later is easy, while taking access away breaks the code that already uses it.