← Back to Blog

Common OOP Mistakes

OOP introduces new ways to think about code. It also introduces new ways to get it wrong. These are the most common mistakes — and what to do instead.

1. Forgetting self

class Animal:
    def __init__(self, name):
        name = name         # wrong — stored as local variable, not on the object

    def describe(self):
        print(self.name)    # AttributeError — self.name doesn't exist

Every attribute must be stored with self.. Without it, the value is a local variable that disappears when __init__ finishes.

class Animal:
    def __init__(self, name):
        self.name = name    # correct

2. Not calling super().__init__()

class Dog(Animal):
    def __init__(self, name, breed):
        self.breed = breed      # Animal.__init__ never runs
                                # self.name, self.age don't exist

dog = Dog("Rex", "Husky")
print(dog.name)                 # AttributeError
class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # runs Animal.__init__ first
        self.breed = breed      # then adds Dog's own attribute

3. Using mutable default arguments in __init__

class Shelter:
    def __init__(self, animals=[]):     # wrong — shared across all instances
        self.animals = animals

s1 = Shelter()
s2 = Shelter()
s1.animals.append("Lassie")
print(s2.animals)               # ['Lassie'] — s2 is affected too

Mutable defaults — lists, dicts — are created once and shared by all instances that don't pass their own value. Always use None as default and create the object inside __init__:

class Shelter:
    def __init__(self, animals=None):
        self.animals = animals if animals is not None else []

4. Modifying class attributes through an instance

class Animal:
    count = 0               # class attribute — shared

a = Animal()
a.count = 5                 # creates an instance attribute — doesn't change the class attribute
print(Animal.count)         # 0 — unchanged
print(a.count)              # 5 — instance attribute shadows the class attribute

If you want to modify a class attribute, do it through the class — not an instance:

Animal.count = 5            # correct — modifies the class attribute
print(Animal.count)         # 5

5. Putting too much in one class

class Animal:
    def __init__(self, name):
        self.name = name

    def describe(self): ...
    def save_to_file(self): ...
    def load_from_file(self): ...
    def send_email_report(self): ...
    def generate_pdf(self): ...
    def connect_to_database(self): ...

A class that does everything is a class that does nothing well. Each class should have one clear responsibility. File handling, email, PDF generation — those belong in separate classes or modules. Animal should know about animals, not about email servers.

6. Accessing private attributes directly

class Animal:
    def __init__(self, name, health):
        self.name    = name
        self._health = health       # private by convention

    def set_health(self, health):
        if health in ["healthy", "ill"]:
            self._health = health

lassie = Animal("Lassie", "healthy")
lassie._health = "missing"          # bypasses validation — wrong

The underscore is a signal — not a lock. Respect it. Use the setter.

7. Confusing the class and the instance

class Animal:
    def describe(self):
        print("I am an animal.")

Animal.describe()       # TypeError — no instance, self has no value
Animal().describe()     # correct — creates an instance, then calls the method

Methods live on instances, not on the class directly. You need an object to call a method on.

8. Not defining __str__

lassie = Animal("Lassie", "dog", 4)
print(lassie)       # <__main__.Animal object at 0x10b3c2d50>

Always define __str__. It costs one method. It pays off every time you print, log, or debug.

What you should understand now

  • Always use self.attr — not just attr — in __init__
  • Always call super().__init__() in child classes
  • Never use mutable objects as default arguments — use None
  • Modify class attributes through the class, not an instance
  • One class, one responsibility
  • Respect the underscore convention — use setters
  • Always define __str__
[ login to bookmark ] // copied! 33 views · 2 min
// resources
Other oop_mistakes.py
← prev Safe Paws Update — Round 2/2 next → OOP — The Full Picture
// 0 comments
// No comments yet. Be the first.
// leave a comment

// Your comment will appear after approval.