Introduction
Getting all attributes of an object in Python is useful for debugging, serialization, and dynamic inspection. The primary tools are dir(), which lists all attribute names including inherited and special ones, and vars(), which returns the instance's __dict__ containing only its own attributes. For more detailed inspection, the inspect module provides functions to distinguish between methods, properties, and data attributes. Understanding these tools helps you explore unfamiliar objects and build dynamic, reflection-based code.
Using dir()
dir() returns a sorted list of all attribute names for an object, including inherited attributes and special (dunder) methods:
1class Animal:
2 species = "Unknown"
3
4 def __init__(self, name, age):
5 self.name = name
6 self.age = age
7
8 def speak(self):
9 return f"{self.name} makes a sound"
10
11dog = Animal("Rex", 5)
12
13print(dir(dog))
14# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
15# '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__',
16# '__hash__', '__init__', '__init_subclass__', ..., 'age', 'name', 'speak',
17# 'species']
To filter out dunder attributes:
1# Only user-defined attributes and methods
2public_attrs = [a for a in dir(dog) if not a.startswith("_")]
3print(public_attrs)
4# ['age', 'name', 'speak', 'species']
Using vars()
vars() returns the __dict__ of an object — only the instance attributes, not class attributes or methods:
1print(vars(dog))
2# {'name': 'Rex', 'age': 5}
3
4# Equivalent to:
5print(dog.__dict__)
6# {'name': 'Rex', 'age': 5}
Notice that species (class attribute) and speak (method) are not in vars() because they belong to the class, not the instance.
Comparing dir() and vars()
1class Car:
2 wheels = 4 # Class attribute
3
4 def __init__(self, make, model):
5 self.make = make # Instance attribute
6 self.model = model # Instance attribute
7
8 def start(self):
9 return f"{self.make} {self.model} started"
10
11car = Car("Toyota", "Camry")
12
13# dir() — everything including inherited
14dir_attrs = [a for a in dir(car) if not a.startswith("_")]
15print(dir_attrs)
16# ['make', 'model', 'start', 'wheels']
17
18# vars() — instance attributes only
19print(vars(car))
20# {'make': 'Toyota', 'model': 'Camry'}
| Feature | dir() | vars() |
| Instance attributes | Yes | Yes |
| Class attributes | Yes | No |
| Methods | Yes | No |
| Inherited attributes | Yes | No |
| Special methods | Yes | No |
| Returns | List of strings | Dictionary |
Using getattr() for Dynamic Access
1class Config:
2 def __init__(self):
3 self.debug = True
4 self.host = "localhost"
5 self.port = 8080
6
7config = Config()
8
9# Access attribute by name (string)
10value = getattr(config, "host")
11print(value) # "localhost"
12
13# With a default for missing attributes
14value = getattr(config, "missing_attr", "default_value")
15print(value) # "default_value"
16
17# Iterate over all instance attributes
18for attr, value in vars(config).items():
19 print(f"{attr} = {value}")
20# debug = True
21# host = localhost
22# port = 8080
Using the inspect Module
The inspect module provides detailed attribute classification:
1import inspect
2
3class MyClass:
4 class_var = 42
5
6 def __init__(self):
7 self.instance_var = "hello"
8
9 def method(self):
10 pass
11
12 @staticmethod
13 def static_method():
14 pass
15
16 @classmethod
17 def class_method(cls):
18 pass
19
20 @property
21 def computed(self):
22 return self.instance_var.upper()
23
24obj = MyClass()
25
26# Get all members with their types
27for name, value in inspect.getmembers(obj):
28 if not name.startswith("_"):
29 print(f"{name}: {type(value).__name__}")
30# class_method: method
31# class_var: int
32# computed: str
33# instance_var: str
34# method: method
35# static_method: function
Filtering by Type
1# Only methods
2methods = inspect.getmembers(obj, predicate=inspect.ismethod)
3print([name for name, _ in methods if not name.startswith("_")])
4# ['class_method', 'method']
5
6# Only data attributes (not methods or properties)
7data_attrs = {k: v for k, v in vars(obj).items()}
8print(data_attrs)
9# {'instance_var': 'hello'}
Using slots
Objects with __slots__ do not have __dict__, so vars() fails:
1class Point:
2 __slots__ = ("x", "y")
3
4 def __init__(self, x, y):
5 self.x = x
6 self.y = y
7
8p = Point(3, 4)
9
10# vars() fails with __slots__
11# vars(p) # TypeError: vars() argument must have __dict__ attribute
12
13# dir() still works
14print([a for a in dir(p) if not a.startswith("_")])
15# ['x', 'y']
16
17# Access slots directly
18print(p.x, p.y) # 3 4
19
20# Get all slot values
21for slot in p.__slots__:
22 print(f"{slot} = {getattr(p, slot)}")
23# x = 3
24# y = 4
Practical Example: Object to Dictionary
1def obj_to_dict(obj):
2 """Convert an object's public attributes to a dictionary."""
3 if hasattr(obj, "__dict__"):
4 return {k: v for k, v in vars(obj).items() if not k.startswith("_")}
5 elif hasattr(obj, "__slots__"):
6 return {s: getattr(obj, s) for s in obj.__slots__
7 if hasattr(obj, s) and not s.startswith("_")}
8 return {}
9
10class User:
11 def __init__(self, name, email):
12 self.name = name
13 self.email = email
14 self._internal = "hidden"
15
16user = User("Alice", "[email protected]")
17print(obj_to_dict(user))
18# {'name': 'Alice', 'email': '[email protected]'}
Common Pitfalls
Assuming vars() shows all attributes: vars() only returns instance attributes stored in __dict__. Class attributes, methods, properties, and __slots__ attributes are not included. Use dir() for a complete list.
Using vars() on objects with __slots__: Objects with __slots__ do not have __dict__, so vars() raises TypeError. Check with hasattr(obj, '__dict__') first, or use dir() and getattr().
Modifying __dict__ directly: While obj.__dict__["new_attr"] = value works, it bypasses descriptors, properties, and __setattr__. Use setattr(obj, "new_attr", value) for safe attribute setting.
Confusing class attributes with instance attributes: dir() shows both, vars() shows only instance attributes. If you modify a class attribute through an instance (obj.class_var = new_value), it creates an instance attribute that shadows the class one.
Not filtering dunder attributes: dir() includes dozens of special methods (__init__, __repr__, __eq__, etc.). Filter with [a for a in dir(obj) if not a.startswith("_")] to see only user-defined attributes.
Summary
dir(obj) lists all attribute names including inherited and special attributes
vars(obj) returns __dict__ with instance attributes only (fails on __slots__ objects)
getattr(obj, name, default) accesses attributes dynamically by name
inspect.getmembers(obj) provides detailed classification of methods, properties, and data
Filter dir() results with not a.startswith("_") to see only public, user-defined attributes