Course Content
Python Essentials for AI Engineer
6 sections · 48 lessons
What are Instance Variables and Methods?
What you need to know
Instance vs class variables
1class ApiClient:2 MAX_RPM = 60 # class variable: one value, shared34 def __init__(self, api_key):5 self.api_key = api_key # instance variable: per object6 self.calls = 078 def call(self): # instance method9 self.calls += 110 return self.calls <= self.MAX_RPM1112a, b = ApiClient("key-a"), ApiClient("key-b")13a.call(); a.call(); b.call()14print(a.calls, b.calls, ApiClient.MAX_RPM) # 2 1 60How attribute lookup works
When you read a.MAX_RPM, Python looks in the instance's own __dict__ first, then in the class, then in parent classes. If none has it, you get AttributeError.
Assigning through the instance, a.MAX_RPM = 10, does not change the class variable. It creates a new instance variable on a that hides the class one for a only. Mutating through the instance is different: a.some_list.append(x) changes the one shared list.
The three kinds of method
| Kind | First parameter | Can use | Typical use |
|---|---|---|---|
| Instance method | self | instance and class data | most methods |
@classmethod | cls | class data | alternative constructors like from_config |
@staticmethod | none | only its arguments | helper that belongs with the class |
1class Tokenizer:2 def __init__(self, vocab):3 self.vocab = vocab45 @classmethod6 def from_words(cls, words):7 return cls({w: i for i, w in enumerate(words)})89 @staticmethod10 def normalise(text):11 return text.lower().strip()1213tok = Tokenizer.from_words(["refund", "upi"])14print(tok.vocab, Tokenizer.normalise(" UPI ")) # {'refund': 0, 'upi': 1} upiA real-life example
A gateway calls an LLM on behalf of many customers and tracks request timestamps per API key. The first version put the log in the class body:
1class RateLimiter:2 log = [] # BUG: one list for EVERY instance34 def __init__(self, key):5 self.key = key67 def record(self, ts):8 self.log.append(ts) # appends to the shared class list9 return len(self.log)1011a, b = RateLimiter("customer-a"), RateLimiter("customer-b")12for t in range(5):13 a.record(t)14print(b.record(99)) # 6 -> customer B "used" A's requestsCustomer A's burst counted against every customer, and quiet customers started getting throttled. The fix is one line: create self.log = [] inside __init__, so each limiter owns its list. Keep class variables for values that really are shared, such as MAX_RPM = 60.
Follow-up questions to expect
- "What happens if you assign to a class variable through
self?" — You create an instance variable with the same name that shadows the class variable for that one object; the class value is unchanged. - "When do you use
@classmethodvs@staticmethod?" —@classmethodwhen you need the class, typically to construct instances;@staticmethodfor a helper that needs neither class nor instance. - "Where are instance variables stored?" — In each object's
__dict__, unless the class uses__slots__to save memory.