vars()
The direct pointer to __dict__ — instance-only attributes, declaration order, live view.
Common call
attrs = vars(obj)
Returns
a dict — LIVE view of __dict__
Replaces
`obj.__dict__` — the same result, just less punctuation
Watch out
the result is LIVE — mutating it mutates the object; not all objects have __dict__
vars([object])
→ dict
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| object | Any | no | An object with a __dict__ attribute (most user classes, modules, and instances). Without an argument, returns locals(). Built-in types (int, list, str, ...) do NOT have __dict__ and raise TypeError. |
Return value
dict — The __dict__ attribute of the given object. Without an argument, returns the local scope (like locals()). With an object: returns its __dict__ — the instance-specific writable attribute namespace. Not all objects have __dict__ (e.g. built-in types like int, list, str).
Common patterns
Inspect an instance's data
Quick way to see all instance attributes without printing methods.
print(vars(user)) # {"name": "Alice", "age": 30}
Serialize a simple dataclass
vars gives you the field dict directly.
import json json.dumps(vars(obj))
Compare instance state
Two instances have equal state when their vars are equal.
vars(a) == vars(b)
Mutate through vars
The returned dict is LIVE — assigning through it changes the object.
vars(obj)["new_attr"] = value # same as: obj.new_attr = value
Examples
1. Instance
class C: pass
c = C()
c.x = 1
vars(c)
Returns
{'x': 1}2. Class
class C:
x = 1
def m(self): pass
vars(C)
Returns
{'x': 1, 'm': <function C.m>, ...}3. Empty instance
class C: pass
vars(C())
Returns
{}4. No argument
vars()
Returns
same as locals()5. Built-in raises
vars(42)
Returns
TypeError: vars() argument must have __dict__ attribute6. Live mutation
vars(obj)["y"] = 5
obj.y
Returns
5 # dict is LIVEPitfalls
1. Built-in immutable types have NO __dict__
int, float, str, tuple, and most other built-in types are implemented in C and lack the Python-level __dict__. Calling vars() on them raises TypeError.
Rejected
vars(42)
TypeError: vars() argument must have __dict__ attribute
Use dir instead
dir(42)
list of names on int
2. vars() vs dir() — very different
vars returns just the __dict__ (instance-only, writable attributes). dir walks the whole MRO (all attributes accessible via getattr, including inherited methods). They return different things almost always.
Assumed same
set(vars(obj)) == set(dir(obj))
False for almost any object
Different tools
vars(obj) # just instance attrs dir(obj) # all accessible names
3. The returned dict is LIVE — mutations affect the object
vars does not copy — it returns the actual __dict__. Assigning through it modifies the object. Sometimes useful, sometimes surprising.
Snapshot lost
snapshot = vars(obj) obj.x = 99 snapshot["x"]
99 # the snapshot updated too
Copy for a snapshot
snapshot = dict(vars(obj))
independent copy
4. Classes with __slots__ have no __dict__
__slots__ classes intentionally lack __dict__ for memory efficiency. Instances of such classes cannot be inspected with vars().
Slotted rejected
class Point: __slots__ = ("x", "y") vars(Point())
TypeError: vars() argument must have __dict__ attribute
Use dir + getattr
{s: getattr(obj, s) for s in obj.__slots__}
equivalent dict
When to use
Use it
- Inspecting instance state during debugging
- Serializing simple "data bag" objects to JSON or repr
- Iterating over an object's writable attributes
- Building an equality check based on instance state
Reach for something else
- Object might not have __dict__ → guard with hasattr(obj, "__dict__")
- You want inherited methods too → dir(obj)
- You want a snapshot → wrap in dict() to copy
- __slots__ classes → attribute-by-attribute access
Notes
Complexity
O(1) — direct attribute access
Return
The live __dict__ mapping — same object each call
CPython impl
Python/bltinmodule.c :: builtin_vars — returns tp_dict or object.__dict__
Memory
No allocation — returns the existing dict
Thread-safe
Depends on whether the underlying dict is safe from concurrent mutation
FAQ
vars gives you the __dict__ — just the instance-specific writable attributes, in declaration order (Python 3.7+). dir walks the whole MRO to return every accessible name (including inherited methods), sorted alphabetically. Both are useful; they answer different questions.
History
1.0
vars() has been a builtin since Python 1.0.
3.7
Class __dict__ preserves declaration order (as an ordered dict was already since 3.6, guaranteed in 3.7).