The four OOP pillars
Encapsulation hides state and exposes methods. Inheritance reuses and extends behaviour. Polymorphism lets subclasses share an interface with different implementations. Abstraction hides complexity behind simple interfaces.
Model domains with classes, instances, and the four OOP pillars.
About 2 min of reading
Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.
Classes let me model a whole kind of thing. I define a class with an initializer and a method, make an instance and call it, and watch inheritance reuse behaviour in a subclass. I also expose a value with a property, compare shared class state to per-instance state, and see polymorphism let two animals speak their own way.
A class bundles data and behaviour. I define __init__ to store name and a method that uses it, then make one instance and print its voice and its type to see the blueprint become a real object.
Before you run it, say out loud what it will print. Then open the answer.
A subclass can copy a parent's behaviour and change just one part. I make a Cat inherit Animal, keep the inherited name, override its speak, and print the name and voice to see what was reused and what was replaced.
Before you run it, say out loud what it will print. Then open the answer.
A property turns a method into a value you can read like an attribute. I start with a balance, deposit money, and read it back through the property to see the getter compute the current amount.
Before you run it, say out loud what it will print. Then open the answer.
Class attributes are shared by every instance while instance attributes are unique to one. I bump a shared counter, then print the shared value and each object's own value to see what is common and what is personal.
Before you run it, say out loud what it will print. Then open the answer.
Polymorphism lets every animal speak its own way behind one speak() name. I make a plain Animal and a Dog and print what each says to see the same method call produce different output.
Before you run it, say out loud what it will print. Then open the answer.
Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.
Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.
Encapsulation hides state and exposes methods. Inheritance reuses and extends behaviour. Polymorphism lets subclasses share an interface with different implementations. Abstraction hides complexity behind simple interfaces.
Class attributes are shared across all instances; instance attributes (set in __init__) are unique per object:
class Counter:
count = 0 # class attribute, shared
def __init__(self):
self.value = 0 # instance attribute
Use class attributes for constants or shared counters, instance attributes for per-object data.
class Dog:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} says woof"
dog = Dog("Rex")
print(dog.speak()) # Rex says woof
__init__ is the constructor; self is the instance. Methods receive the instance as the first argument.
class Animal:
def __init__(self, name):
self.name = name
class Cat(Animal):
def speak(self):
return f"{self.name} says meow"
c = Cat("Milo")
print(c.name, c.speak()) # Milo says meow
Cat inherits Animal's __init__ and overrides speak.
class Bank:
def __init__(self, balance):
self._balance = balance # "private" by convention
@property
def balance(self):
return self._balance
def deposit(self, amount):
self._balance += amount
b = Bank(100)
b.deposit(50)
print(b.balance) # 150
The _ prefix signals private; @property adds computed, safe access.
Each one is defined here on this site. Open the entry for the full definition and the other words it points at.