Python
Object Iteration
Attributes
Code Duplication
Programming Tips

Iterate over object attributes in python

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Python provides several ways to iterate over an object's attributes: vars() returns an object's __dict__ (instance attributes only), dir() returns all attributes including inherited ones, inspect.getmembers() gives attribute name-value pairs with filtering, and __dict__ can be accessed directly. The right choice depends on whether you need instance attributes only, class attributes, methods, or everything.

vars() — Instance Attributes

vars(obj) returns the object's __dict__, which contains only instance attributes:

python
1class User:
2    def __init__(self, name, age, email):
3        self.name = name
4        self.age = age
5        self.email = email
6
7user = User("Alice", 30, "[email protected]")
8
9# Get all instance attributes as a dict
10print(vars(user))
11# {'name': 'Alice', 'age': 30, 'email': '[email protected]'}
12
13# Iterate over attributes
14for attr, value in vars(user).items():
15    print(f"{attr} = {value}")
16# name = Alice
17# age = 30
18# email = [email protected]

vars() does not include class-level attributes, methods, or properties — only what is assigned to self in __init__ (or later).

dict — Direct Access

Equivalent to vars(), accessing __dict__ directly:

python
for key, value in user.__dict__.items():
    print(f"{key}: {value}")

__dict__ and vars() return the same dictionary. vars() is preferred because it is more readable and works with built-in types like modules.

dir() — All Attributes Including Inherited

dir(obj) returns a list of all attribute names, including inherited methods and dunder attributes:

python
1class Animal:
2    species = "Unknown"
3
4    def __init__(self, name):
5        self.name = name
6
7    def speak(self):
8        pass
9
10class Dog(Animal):
11    def __init__(self, name, breed):
12        super().__init__(name)
13        self.breed = breed
14
15dog = Dog("Rex", "Labrador")
16
17# dir() includes everything
18print(dir(dog))
19# ['__class__', '__delattr__', ..., 'breed', 'name', 'speak', 'species']
20
21# Filter to user-defined attributes (exclude dunders)
22attrs = [a for a in dir(dog) if not a.startswith('_')]
23print(attrs)  # ['breed', 'name', 'speak', 'species']

dir() returns names only (no values). To get values, use getattr():

python
1for attr in dir(dog):
2    if not attr.startswith('_'):
3        print(f"{attr} = {getattr(dog, attr)}")
4# breed = Labrador
5# name = Rex
6# speak = <bound method Animal.speak of ...>
7# species = Unknown

inspect.getmembers() — Filtered Iteration

The inspect module provides getmembers() with optional predicates:

python
1import inspect
2
3class Calculator:
4    pi = 3.14159
5
6    def __init__(self, value=0):
7        self.value = value
8
9    def add(self, x):
10        self.value += x
11
12    @property
13    def doubled(self):
14        return self.value * 2
15
16calc = Calculator(10)
17
18# All non-private members
19members = inspect.getmembers(calc, lambda m: not callable(m))
20print(members)
21# [('pi', 3.14159), ('value', 10)]
22
23# Only methods
24methods = inspect.getmembers(calc, predicate=inspect.ismethod)
25print(methods)
26# [('add', <bound method Calculator.add of ...>)]

Predicates like inspect.ismethod, inspect.isfunction, and inspect.isclass let you filter precisely.

Filtering Attributes by Type

python
1class Config:
2    def __init__(self):
3        self.host = "localhost"
4        self.port = 8080
5        self.debug = True
6        self.timeout = 30.0
7
8config = Config()
9
10# Only string attributes
11strings = {k: v for k, v in vars(config).items() if isinstance(v, str)}
12print(strings)  # {'host': 'localhost'}
13
14# Only numeric attributes
15numbers = {k: v for k, v in vars(config).items() if isinstance(v, (int, float))}
16print(numbers)  # {'port': 8080, 'timeout': 30.0}

Serialization Use Case

A common reason to iterate attributes is to serialize an object:

python
1import json
2
3class Product:
4    def __init__(self, name, price, in_stock):
5        self.name = name
6        self.price = price
7        self.in_stock = in_stock
8
9product = Product("Widget", 9.99, True)
10
11# Convert to dict
12product_dict = vars(product)
13print(json.dumps(product_dict, indent=2))
14# {
15#   "name": "Widget",
16#   "price": 9.99,
17#   "in_stock": true
18# }
19
20# Rebuild from dict
21restored = Product(**product_dict)

For production serialization, use dataclasses or pydantic:

python
1from dataclasses import dataclass, asdict
2
3@dataclass
4class Product:
5    name: str
6    price: float
7    in_stock: bool
8
9product = Product("Widget", 9.99, True)
10print(asdict(product))  # {'name': 'Widget', 'price': 9.99, 'in_stock': True}

slots Classes

Classes using __slots__ do not have __dict__:

python
1class Point:
2    __slots__ = ('x', 'y')
3
4    def __init__(self, x, y):
5        self.x = x
6        self.y = y
7
8p = Point(1, 2)
9# vars(p)  # TypeError: vars() argument must have __dict__ attribute
10
11# Use __slots__ to iterate instead
12for slot in p.__slots__:
13    print(f"{slot} = {getattr(p, slot)}")
14# x = 1
15# y = 2

Common Pitfalls

  • Using vars() on __slots__ objects: Classes with __slots__ have no __dict__, so vars() raises TypeError. Iterate over obj.__slots__ with getattr() instead.
  • Modifying __dict__ during iteration: Changing attributes (adding or deleting) while iterating over vars(obj).items() can cause RuntimeError. Iterate over a copy: list(vars(obj).items()).
  • Expecting vars() to include class attributes: vars(obj) only returns instance attributes. Class attributes (defined outside __init__) and inherited attributes are excluded. Use dir() to see everything.
  • Including methods when you want data only: dir() and getattr() return methods alongside data attributes. Filter with not callable(getattr(obj, attr)) to exclude methods.
  • Properties executing during iteration: Accessing a @property via getattr() triggers the getter, which may have side effects or raise exceptions. Use inspect.getmembers_static() (Python 3.11+) or check the class descriptor to identify properties before accessing them.

Summary

  • Use vars(obj) or obj.__dict__ for instance attributes as a dictionary
  • Use dir(obj) for all attribute names including inherited ones and methods
  • Use inspect.getmembers(obj, predicate) for filtered name-value pairs
  • Use getattr(obj, name) to get the value of an attribute by name from dir() results
  • For __slots__ classes, iterate over obj.__slots__ since __dict__ does not exist
  • Use dataclasses.asdict() for clean serialization instead of manual attribute iteration

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.