Quick answer: vars() returns an object’s __dict__ namespace when available, or a current local namespace in supported no-argument contexts. Use it for deliberate inspection, check for missing __dict__, and avoid mutating returned state when documented setters or validation are required.

Python vars() is a built-in function for inspecting the writable attributes stored on many Python objects. In everyday debugging, it is most useful when you want to see the data attached to an instance without manually reading each attribute one by one.
The short rule is simple: vars(obj) returns the object's __dict__ attribute when that attribute exists. Called with no argument, vars() behaves like locals() in the current scope. The official Python documentation for vars() describes this behavior directly, and the data model documentation for __dict__ explains where those attributes live.
What Python vars() Returns
For a normal class instance, vars() usually returns the same dictionary you would get from obj.__dict__. That dictionary contains instance attributes, not every method or inherited class attribute. It is a snapshot of the attributes currently stored on that instance.
class User:
pass
user = User()
user.name = "Ada"
user.score = 98
print(vars(user))
This example returns a dictionary with name and score because those values were assigned directly to the instance. If you are learning how objects are built, this is a cleaner inspection tool than printing many attributes separately.
Use vars() With Classes and Instances
Classes and instances expose different data. An instance dictionary stores per-object values. A class namespace stores methods, class variables, and metadata such as __module__. That difference matters when you debug object-oriented code, especially if you also use inheritance with tools like Python super().
class Counter:
kind = "metric"
def __init__(self, name, value):
self.name = name
self.value = value
counter = Counter("visits", 12)
print(vars(Counter)["kind"])
print(vars(counter))
Notice that vars(Counter) inspects the class namespace, while vars(counter) inspects one created object. For class-heavy designs, compare this with related object concepts such as nested classes in Python and Python callable objects.
Use vars() With Modules
Modules also keep a namespace dictionary. Passing a module to vars() is a quick way to inspect constants, functions, classes, and imported names inside that module. In real debugging, filter private names so the result stays readable.
import math
public_names = [
name for name in vars(math)
if not name.startswith("_")
]
print(public_names[:6])
This pattern is useful when you are exploring a module interactively. For a more user-facing inspection helper, the Python help() function gives formatted documentation instead of the raw namespace dictionary.

Call vars() With No Argument
When you call vars() without an object, Python returns the local namespace for the current scope. This is close to locals(), so it can help with quick debugging inside a function. Avoid writing production logic that depends on modifying this returned mapping, because changes to local variables through that dictionary are not a reliable programming interface.
def build_record():
language = "Python"
level = "beginner"
record = vars().copy()
return record
print(build_record())
The .copy() call is intentional. It gives you a normal dictionary that will not be affected by later local variable changes. This keeps debugging output predictable.
When vars() Raises TypeError
Not every object has a writable attribute dictionary. Built-in immutable values such as integers and strings do not expose __dict__, so vars() raises TypeError. This is expected behavior, not a bug in the function.
values = [42, "python", ["a", "b"]]
for value in values:
try:
print(vars(value))
except TypeError as exc:
print(type(value).__name__, "does not expose __dict__")
You can guard against this by checking hasattr(obj, "__dict__") before calling vars(). That is useful in generic debugging helpers that may receive built-in values, custom classes, or third-party objects. It also keeps error handling clear when an object supports attribute lookup through methods, slots, or descriptors instead of a normal dictionary.

vars() vs __dict__, dir(), and locals()
vars(obj) and obj.__dict__ are closely related. For many instances, they point at the same underlying dictionary. dir(obj) is broader: it lists many attribute names, including inherited names and methods. locals() is for the current local scope, while vars() can inspect either a specific object or the current scope when no argument is supplied. vars() exposes one namespace dictionary, while Python inspect Module Examples provides signatures, source locations, members, and callable introspection.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
point = Point(2, 3)
attributes = vars(point)
attributes["x"] = 10
print(point.x)
This example shows why you should treat the returned dictionary carefully. For normal instances, editing the dictionary can edit the object itself. That can be useful in controlled metaprogramming, but for ordinary application code it is clearer to assign attributes directly.
Practical Guidelines
Use vars() for inspection, logging, test assertions, and quick interactive exploration. Do not use it as a replacement for a stable serialization layer, because it may include temporary attributes or miss computed properties. If an object uses __slots__, properties, or custom attribute access, vars() may not show the full behavior of that object.
For clean application code, keep vars() near debugging boundaries. Convert the result to a copy before returning it from a function, avoid mutating another object's dictionary unless you own that object, and prefer explicit attributes when writing business logic. These rules make the helper useful without hiding state changes.
Conclusion
Use Python vars() when you need a direct view of an object's attribute dictionary. It is best for debugging instances, exploring classes or modules, and taking a quick local-scope snapshot. Remember the limits: it only works when an object exposes __dict__, class namespaces are different from instance dictionaries, and mutating the returned mapping can mutate the object. Used with those rules in mind, vars() is a small but practical inspection tool.
Inspect The Namespace
vars(obj) is useful for seeing instance attributes or a module namespace when the object exposes __dict__. It does not reveal every attribute provided dynamically by a type or descriptor.

Understand No-Argument vars
Called without an argument in an appropriate function scope, vars can expose the local namespace. Treat that mapping as an introspection aid rather than a general way to rewrite local variables.
Handle Slots And Extensions
Objects using __slots__ or extension implementations may not have __dict__, so vars raises TypeError. Use documented attributes or a safe inspection method for those types.

Avoid Accidental Mutation
A writable __dict__ mapping can change instance state without invoking normal validation. Prefer public APIs when invariants, caches, or descriptors matter.
Compare dir And vars
dir lists names available through an object’s type and attributes, while vars focuses on one namespace mapping. Neither is a replacement for the class documentation.
Keep Inspection Safe
Do not expose secrets or arbitrary object state in logs, and test custom classes, slots, properties, inherited attributes, modules, and empty namespaces before relying on introspection.
Use the official Python vars documentation and dir documentation. Related Python Pool references include dictionaries and testing.
For related introspection work, compare namespace mappings, object tests, and attribute collections before using vars().
Frequently Asked Questions
What does vars() do in Python?
vars() returns the __dict__ namespace for an object when available, or the current local namespace when called without an argument in an appropriate scope.
Why does vars() raise an error for some objects?
Objects implemented with slots or extension types may not provide __dict__, so vars() cannot return an instance namespace for them.
Can I modify an object through vars()?
The returned mapping may expose writable instance attributes, but mutation bypasses normal validation and should be done only when the object’s contract permits it.
Is vars() the same as dir()?
No. vars focuses on a namespace mapping, while dir lists names available through an object’s attributes and type behavior; neither is a substitute for documented APIs.