Classes, Instances, self and Attributes
45 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Interviewers open OOP rounds with the basics because mistakes here show up everywhere else. Expect "what is self", "class versus instance attribute" and a code snippet to predict.
Class, instance and the two kinds of attribute
Instance is one object built from a class. It has its own attributes, stored in its own __dict__, and a link back to its class.
An interviewer starts here to see if you know where data lives. A class is a blueprint. Each call to the class builds one instance with its own attributes.
class Account: bank = "MentorBank" # class attribute
def __init__(self, owner, balance): self.owner = owner # instance attributes self.balance = balance
a = Account("Asha", 500)b = Account("Ravi", 200)print(a.owner, b.owner, a.bank)Example 1 · Two instances, one class
class Account: bank = "MentorBank"
def __init__(self, owner, balance): self.owner = owner self.balance = balance
a = Account("Asha", 500)b = Account("Ravi", 200)print(a.owner, a.balance, a.bank)print(b.owner, b.balance, b.bank)print(a is b)Asha 500 MentorBank Ravi 200 MentorBank False
What this code does
- Each call builds a new object, so
a is bis False. bankcomes from the class; the other values come from each instance.
bank is stored once on the class and every instance reads it. owner and balance are stored on each instance.
What self really is
self is the first parameter of an instance method. Python passes the object the method was called on into it, so the method can read and change that object.
self is not a keyword. It is the first parameter name by convention, and Python fills it with the object before the dot.
class Account: def __init__(self, balance): self.balance = balance
def deposit(self, amount): self.balance += amount
a = Account(500)a.deposit(50)Account.deposit(a, 50)print(a.balance)a.deposit(50) is shorthand for Account.deposit(a, 50). That is why a method needs self in its definition: the object is passed as an argument.
Class attribute traps
Attribute lookup is how Python finds obj.name: first in the instance, then in its class, then in the parent classes. The first match wins.
Reading obj.name looks in the instance first, then the class. Assigning obj.name = value always writes to the instance. A mutable class attribute is shared by everyone.
class Cart: items = [] # shared by all carts
def add(self, item): self.items.append(item)
a, b = Cart(), Cart()a.add("pen")print(b.items)Example 2 · Assigning shadows the class attribute
class Account: bank = "MentorBank"
a = Account()b = Account()a.bank = "OtherBank"print(a.bank, b.bank, Account.bank)print(a.__dict__, b.__dict__)OtherBank MentorBank MentorBank
{'bank': 'OtherBank'} {}What this code does
a.bank = ...creates a new attribute onaonly. The class value is unchanged.b.__dict__is empty, sob.bankstill falls back to the class.
Example 3 · The shared list bug and its fix
class BadCart: items = []
def add(self, item): self.items.append(item)
class Cart: def __init__(self): self.items = []
def add(self, item): self.items.append(item)
x, y = BadCart(), BadCart()x.add("pen")print(y.items)p, q = Cart(), Cart()p.add("pen")print(q.items)['pen'] []
What this code does
BadCartshares one list across all instances.Cartbuilds a fresh list per instance inside__init__.
No assignment happened, only append on the one shared list, so b sees the pen. Create the list inside __init__ to give each cart its own.
Next step
Check your understanding
12 quick questions