dir() in Python 2026: Introspection & Object Attribute Listing + Modern Use Cases
The built-in dir() function returns a sorted list of valid attribute names for an object — the most basic and powerful introspection tool in Python. In 2026 it remains indispensable for debugging, REPL exploration, dynamic attribute access, metaprogramming, testing, and IDE-like functionality in scripts or notebooks.
With Python 3.12–3.14+ improving attribute lookup performance, enhancing free-threading safety for introspection, and better support for type annotations on dynamic objects, dir() is more reliable and useful than ever in concurrent code, plugin systems, and AI-assisted development. This March 23, 2026 update explains how dir() behaves today, real-world patterns, filtering techniques, and best practices for clean, maintainable introspection in modern Python.
TL;DR — Key Takeaways 2026
dir(obj)→ returns list[str] of attribute/method names (sorted)dir()with no argument → lists current scope (locals + globals)- 2026 best practice: Use dir() for debugging & exploration; filter results for production code
- Main use cases: interactive debugging, dynamic dispatch, plugin inspection, testing assertions
- Performance: Fast — C-level, but avoid in hot loops due to list creation
1. Basic Usage — Listing Attributes
class Person:
def __init__(self, name):
self.name = name
def greet(self):
return f"Hello, {self.name}"
p = Person("Alice")
print(dir(p))
# ['__class__',
# '__delattr__',
# '__dict__',
# ...,
# 'greet',
# 'name']
print(dir()) # current scope attributes
2. Real-World Patterns in 2026
Debugging & REPL Exploration
def inspect(obj):
print(f"Attributes of {type(obj).__name__}:")
for attr in dir(obj):
if not attr.startswith("_"):
print(f" {attr}")
inspect(p)
# Attributes of Person:
# greet
# name
Dynamic Method Dispatch / Plugin Inspection
def find_actions(obj):
return [name for name in dir(obj) if callable(getattr(obj, name)) and not name.startswith("_")]
print(find_actions(p)) # ['greet']
Testing / Assertion Helpers
def assert_has_attr(obj, attr_name: str):
if attr_name not in dir(obj):
raise AssertionError(f"Missing attribute: {attr_name}")
assert_has_attr(p, "greet")
3. dir() vs Alternatives – Comparison 2026
| Approach | Output | Dynamic? | Best For |
|---|---|---|---|
| dir(obj) | sorted list of str | Yes | General introspection |
| obj.__dict__.keys() | dict_keys of instance attrs | Yes | Only instance attributes |
| vars(obj) | dict of instance attrs | Yes | Readable dict view (not all objects) |
| inspect.getmembers(obj) | list of (name, value) tuples | Yes | Detailed inspection (value included) |
4. Best Practices & Performance in 2026
- Filter out dunders —
[a for a in dir(obj) if not a.startswith("_")] - Type hints 2026:
from typing import Any, Iterable def public_attrs(obj: Any) -> list[str]: return [a for a in dir(obj) if not a.startswith("_")] - Performance: dir() is fast but creates a list — avoid in very hot loops
- Free-threading (3.14+): Safe for reads; use locks if modifying attributes concurrently
- Avoid: dir() on very large objects (many attributes) — can be slow to list
Worked Example: dir() vs vars() vs inspect
dir(obj) lists names you can access on an object, including inherited ones and methods from the class, sorted alphabetically. vars(obj) returns only the instance's own __dict__, the data actually stored on it. inspect.getmembers() returns name/value pairs and can filter by kind. A class can also customise dir() by defining __dir__, so treat its output as a helpful listing, not a guaranteed contract.
import inspect
class Base:
kind = "base"
def hello(self):
return "hi"
class Item(Base):
def __init__(self, sku, price):
self.sku = sku
self.price = price
def total(self, qty):
return self.price * qty
it = Item("A1", 9.5)
public = [n for n in dir(it) if not n.startswith("_")]
print(public) # ['hello', 'kind', 'price', 'sku', 'total']
print(vars(it)) # {'sku': 'A1', 'price': 9.5}
methods = [name for name, _ in inspect.getmembers(it, inspect.ismethod)]
print(methods) # ['__init__', 'hello', 'total']
print("kind" in vars(it), hasattr(it, "kind")) # False True
The last line shows the difference clearly: kind is reachable on the instance but not stored on it.
Common Mistakes
- Using
name in dir(obj)as an existence test:hasattr()is faster and respects__getattr__ - Expecting
dir()to show only data: it mixes methods and attributes - Relying on
dir()output in production logic: it is designed for interactive use
Quick Summary
dir()= everything accessible, sortedvars()= only the object's own attributesinspect.getmembers()= filtered name/value pairs
Conclusion — dir() in 2026: Introspection Essential
dir() is the quickest way to discover what an object can do — perfect for debugging, REPL exploration, dynamic code, and testing. In 2026, use it with filtering, type hints, and caution in performance-critical paths. It’s simple, fast, and one of Python’s most powerful introspection helpers — essential for understanding any unknown object.
Next steps:
- Run dir() on your next unfamiliar object or module