The vars([object]) function returns the __dict__ attribute of an object, exposing its underlying namespace as a dictionary.
Namespace Introspection: When called without arguments, vars() returns the current local scope dictionary, identical to locals(). When passed a module, class, or instance, it reveals that object's namespace mapping. If the supplied object does not possess a __dict__ attribute, Python raises a TypeError. For related introspection tools, see getattr(), setattr(), hasattr(), and dir().
The vars() function accepts an optional object argument:
namespace = vars([object])
| Parameter | Type Accepted | Behavioral Details |
|---|---|---|
object (optional) |
Module, class, instance, or omitted | The target object whose __dict__ is retrieved. If omitted, returns the current local scope. |
| Return Value | dict or mappingproxy |
Returns a mutable dictionary for instances and local scopes, or a read-only mappingproxy for classes. |
Passing an instance of a user-defined class returns its internal instance dictionary:
class Animal:
species = "carnivores" # Class attribute
def __init__(self, name: str, age: int):
self.name = name
self.age = age
tiger = Animal("Ronald", 5)
# Inspect the instance namespace
print("Instance dictionary:", vars(tiger))
Output:
Instance dictionary: {'name': 'Ronald', 'age': 5}
Because the dictionary returned for instances is the live __dict__, updating it mutates object state:
# In-place attribute modification via vars() dictionary
vars(tiger)["name"] = "Leo"
vars(tiger)["weight"] = 220
print("tiger.name updated:", tiger.name)
print("tiger.weight added:", tiger.weight)
Output:
tiger.name updated: Leo
tiger.weight added: 220
vars() Without Arguments: Local Namespace 🔝When invoked with no arguments, vars() acts identically to locals(), returning a dictionary of the current local scope:
def demo_local_scope():
alpha = 100
beta = "plus2net"
gamma = [1, 2, 3]
local_vars = vars()
return local_vars
print("Local variables:", demo_local_scope())
Output:
Local variables: {'alpha': 100, 'beta': 'plus2net', 'gamma': [1, 2, 3]}
mappingproxy 🔝A critical difference occurs when calling vars() on a class object versus an instance:
vars(instance) returns a standard mutable dict.vars(Class) returns a mappingproxy (a read-only dictionary view protecting class namespace integrity).class Device:
vendor = "Acme Corp"
dev = Device()
# 1. Instance returns mutable dict
vars(dev)["serial"] = "SN-999"
print("Instance dict:", vars(dev))
# 2. Class returns mappingproxy (read-only)
class_ns = vars(Device)
print("Class namespace type:", type(class_ns))
try:
class_ns["vendor"] = "New Corp"
except TypeError as err:
print("Caught mappingproxy error:", err)
Output:
Instance dict: {'serial': 'SN-999'}
Class namespace type: <class 'mappingproxy'>
Caught mappingproxy error: 'mappingproxy' object does not support item assignment
vars() vs. dir() Comparison 🔝While both functions inspect objects, they serve fundamentally different architectural roles:
| Feature | vars(obj) |
dir(obj) |
|---|---|---|
| Return Type | dict (keys and values) |
list (string attribute names only) |
| Scope of Attributes | Only attributes stored directly in obj.__dict__ |
All attributes, including inherited class methods and properties |
Objects Without __dict__ |
Raises TypeError |
Returns valid list of attributes |
| Use Case | Serializing state, inspecting values | Exploring available methods and APIs |
__slots__ 🔝vars() strictly requires that the argument has a __dict__ attribute. Built-in types and slotted classes reject vars():
# 1. Built-in primitive types lack __dict__
try:
vars([1, 2, 3])
except TypeError as err:
print("List TypeError:", err)
# 2. Slotted classes suppress __dict__
class Coordinates:
__slots__ = ("lat", "lon")
def __init__(self, lat: float, lon: float):
self.lat = lat
self.lon = lon
coord = Coordinates(17.385, 78.486)
try:
vars(coord)
except TypeError as err:
print("Slotted TypeError:", err)
Output:
List TypeError: vars() argument must have __dict__ attribute
Slotted TypeError: vars() argument must have __dict__ attribute
Convert custom class instances into clean dictionary payloads for JSON encoding or logging:
def serialize_object(obj) -> dict:
if hasattr(obj, "__dict__"):
return {k: v for k, v in vars(obj).items() if not k.startswith("_")}
return {}
class UserAccount:
def __init__(self, uid: int, email: str):
self.uid = uid
self.email = email
self._internal_token = "secret"
user = UserAccount(101, "dev@plus2net.com")
print("Public payload:", serialize_object(user))
Output:
Public payload: {'uid': 101, 'email': 'dev@plus2net.com'}
Capture an object's instance state as a dictionary snapshot and restore it after failure:
class TransactionRecord:
def __init__(self, amount: float, status: str):
self.amount = amount
self.status = status
def snapshot(self) -> dict:
return vars(self).copy()
def restore(self, backup: dict):
vars(self).clear()
vars(self).update(backup)
tx = TransactionRecord(50.0, "pending")
saved = tx.snapshot()
# Simulate corrupted state
tx.status = "failed"
tx.amount = 0.0
# Rollback to snapshot
tx.restore(saved)
print("Restored status:", tx.status, "Amount:", tx.amount)
Output:
Restored status: pending Amount: 50.0
vars() does NOT reliably change the actual local variables in CPython due to compiler bytecode optimizations (LOAD_FAST).
def buggy_local_mutation():
x = 10
vars()["x"] = 999 # Modifying local vars() dict
return x # Returns 10! The local variable is unchanged
print("Local mutation result:", buggy_local_mutation())
Output:
Local mutation result: 10
Practice namespace inspection, serialization, and state snapshots in our companion Jupyter Notebook:
Explore interactive code cells, compare vars() against dir(), test mappingproxy immutability, and run self-validating assertions.
Run these unit checks directly in Python or your local environment to verify vars() mechanics:
def test_vars_mechanics():
class Person:
def __init__(self, name: str):
self.name = name
p = Person("Alice")
assert vars(p) == {"name": "Alice"}
# Direct mutation via vars dict
vars(p)["name"] = "Bob"
assert p.name == "Bob"
# Primitives raise TypeError
try:
vars(123)
assert False
except TypeError:
pass
print("All vars() unit assertions passed! 100% verified.")
test_vars_mechanics()
Output:
All vars() unit assertions passed! 100% verified.
Returns a sorted list of all valid attribute names for an object, including inherited members.
Retrieves the value of a named attribute from an object with default fallback.
Dynamically assigns or updates a named attribute on an object instance.
Checks whether an object possesses a specific attribute, returning True or False.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.