skip to content

Class vs Instance Attributes

Class-body attributes are shared by every instance until an assignment shadows them on the instance — the classic mutable-class-attribute bug. Interviewers check that you can explain the lookup order out loud.

part ofPythonoverview, primer and where to startread it →
on this pageshow

questions

4

What is the difference between a class attribute and an instance attribute in Python?

level: juniorimportance: must knowfreq 78%

answer

  1. Two separate mappings are involved
  2. The type holds one, the object another
  3. Class body runs once at import
  4. Reads fall back; writes never do
  5. vars(obj) lists only what it owns

basics

~20 s

A class attribute is created in the class body and stored on the class, so every instance sees the same object. An instance attribute is created by assigning to self.name and lives in that one object's own namespace.

solid answer

~40 s

A **class attribute** is bound when the class body runs, once, and is stored in the class's `__dict__`. An **instance attribute** is bound when you assign to `self.name` (usually in `__init__`) and is stored in that object's `__dict__`. Reading `obj.name` looks in the instance `__dict__` first and, failing that, walks the classes in `type(obj).__mro__`, so an instance transparently sees class attributes it does not own. Writing is not symmetric: `obj.name = value` always creates or replaces an entry in the *instance* `__dict__` and shadows the class one, leaving the class value untouched for every other instance. `vars(obj)` shows only what the instance itself owns; `vars(type(obj))` shows what the class owns. Use class attributes for values genuinely shared by all instances - constants, defaults, counters - and instance attributes for per-object state.

code

python · 16 lines
python
class Job:
    retries = 3                 # class attribute: one object, shared

    def __init__(self, name):
        self.name = name        # instance attribute: one per object

a = Job("nightly")
b = Job("hourly")
print(a.retries, vars(a))       # 3 {'name': 'nightly'}

a.retries = 5                   # binds in a's own __dict__
print(a.retries, b.retries, Job.retries)   # 5 3 3
print(vars(a))                  # {'name': 'nightly', 'retries': 5}

del a.retries                   # removes only the instance entry
print(a.retries)                # 3

go deeper

for a junior

Be ready to state the difference in one breath: class body means shared and stored on the class, self.x = ... means per-object. Know that reading an attribute falls back to the class and that assigning never changes it.

for a middle

Explain the mechanics: the class body executes once at import, reads consult the instance mapping then type(obj).mro, and writes always land in the instance mapping. Show the shadowing and the del that undoes it.

for a senior

Demonstrate judgement about what belongs where in real code - immutable shared defaults on the class, per-object mutable state in init - and be able to introspect a live object with vars() to explain surprising values during debugging.

for a principal

Own the API consequence: class attributes are part of a class's public contract that subclasses override by re-declaration, so choosing them over constructor arguments decides how configurable your types are and how easily a subclass can shift behaviour without touching init.

### Two namespaces, not one Every Python object of a normal user-defined class carries its own mapping, reachable as `obj.__dict__` or `vars(obj)`. The class itself is also an object and carries its own mapping, `vars(SomeClass)` (a read-only `mappingproxy`). "Class attribute" and "instance attribute" name *which of those two mappings holds the binding*, nothing more. A class attribute is produced by an ordinary assignment statement inside the class body. The class body is executed exactly once, top to bottom, when the `class` statement runs at import time; the names it binds become the class's namespace. So `retries = 3` in a class body evaluates `3` once, and the resulting object is stored on the class forever - it is not re-evaluated per instance and it is not copied into instances. An instance attribute is produced by assigning to an attribute of an instance - almost always `self.name = value` inside `__init__` or another method, but `obj.name = value` from outside does exactly the same thing. That assignment stores the binding in the instance's own mapping. ### Reading falls back, writing does not The asymmetry between reads and writes is the single most useful thing to be able to state out loud. *Reading* `obj.name` first consults the instance mapping. If the name is not there, Python walks the classes in `type(obj).__mro__` - the method resolution order, the linearized list of the class and its bases - and returns the first binding it finds. Only if no class in that chain has the name does Python raise `AttributeError` (after giving the class a chance to answer through `__getattr__`). This fallback is exactly why instances "inherit" methods: a function defined in the class body is just another class attribute, found by the same walk. *Writing* `obj.name = value` does not follow that chain. It binds the name in the instance mapping, full stop. The class binding is untouched and still visible from every other instance; the new instance binding simply *shadows* it for this one object. That is why a per-instance override is cheap and local, and why you cannot update a class attribute for everybody by assigning through an instance - you have to assign on the class itself, `SomeClass.name = value` or `type(self).name = value`. Deleting is symmetric with writing: `del obj.name` removes only the instance entry, so the class value becomes visible again; a second `del` raises `AttributeError`, because `del` never reaches into the class. ### What each one is for Class attributes are the right home for things that are genuinely one-per-class: constants and configuration defaults, lookup tables, sentinels, and the methods themselves. Because they are shared, mutable class attributes are a well-known trap - a list or dict in a class body is one object that every instance mutates - so keep shared class attributes immutable and build per-object containers in `__init__`. Instance attributes hold per-object state. Creating them in `__init__` rather than lazily in some later method is worth doing for a practical reason: it makes the object's shape obvious to a reader and to tooling, and it avoids `AttributeError` on paths where the later method never ran. ### Introspection `vars(obj)` (equivalently `obj.__dict__`) is the honest answer to "what does this object own?" - it lists instance attributes only, never inherited class ones. `vars(SomeClass)` lists what that one class owns, without its bases. `hasattr(obj, "name")` and `getattr(obj, "name")` answer the *lookup* question and deliberately do not tell you where the value came from; `dir(obj)` merges everything reachable for interactive convenience. ### One 3.14 wrinkle worth knowing A bare annotation in a class body, `timeout: float`, is not an assignment and creates **no attribute at all** - `hasattr(cls, "timeout")` is `False`. It only records an entry in the class's annotations. In Python 3.14 those annotations became lazily evaluated (PEP 649/749), and the supported way to read them is `annotationlib.get_annotations(cls)`. Candidates who read `x: int` in a class body as "a class attribute with no value" are wrong on every version; on 3.14 they are also reaching for something that is no longer eagerly computed.

  • If reading an attribute falls back to the class, why does assigning to it never update the class?
    Because attribute assignment on an instance is defined as a store into that instance's own namespace, not a search. `obj.name = value` binds in `vars(obj)` and stops; the class binding is left alone and stays visible to every other instance. To change the shared value you must assign on the class object itself - `SomeClass.name = value`, or `type(self).name = value` from inside a method.
  • How do you decide whether a value belongs in the class body or in __init__?
    Ask whether the value is genuinely one-per-class or one-per-object. Constants, defaults, lookup tables and methods belong in the class body. Anything that differs between objects, and anything mutable that an object will change, belongs in `__init__` as `self.name = ...`, so each instance gets its own object rather than mutating a shared one.
  • Does a bare annotation such as `count: int` in a class body create a class attribute?
    No. It records an annotation and binds nothing, so `hasattr(cls, "count")` is `False` and touching `cls.count` raises `AttributeError`. Since Python 3.14 those annotations are evaluated lazily (PEP 649/749) and read with `annotationlib.get_annotations(cls)`. Only an assignment such as `count = 0` creates the attribute.

The class attribute is the value printed on the form template; an instance attribute is what one person wrote in the box. Writing in your own box never changes the template, and the template still shows through wherever nobody wrote anything.

saying these in an interview costs you the question

  • Says each new instance gets a copy of every class attribute
  • Thinks self.x = 1 updates the class attribute for all instances
  • Expects vars(obj) to list inherited class attributes
  • Believes the class body re-runs for each instance
  • Reads a bare class-body annotation as creating an attribute
  • Cannot say where the binding is actually stored

context

open as a page

Why does a mutable list defined in a Python class body end up shared by every instance?

level: middleimportance: must knowfreq 66%

basics

~20 s

The list is built once, when the class body runs, and stored on the class. Every instance reaches that same object through self.name, so an append from one is visible from all. Build the list in init instead.

open as a page

Why does self.count += 1 on an integer class attribute create an instance attribute?

level: middleimportance: should knowfreq 52%

basics

~20 s

Augmented assignment is a read then a write. The read falls back to the class attribute, but the write always binds in the instance's own namespace, so the class value never changes and each object ends up with its own counter.

open as a page

How do you tell at runtime whether a Python object's attribute lives on the instance or its class?

level: seniorimportance: should knowfreq 34%

basics

~10 s

Check ownership rather than value: the name is in vars(obj) if the instance owns it, and in vars of some class in type(obj).mro otherwise. getattr and hasattr answer the lookup, not the origin.

open as a page