
Introspection means asking an object about itself while the program is running. It is useful when you are learning an unfamiliar class, checking why an attribute lookup behaves strangely, or debugging a value that is not what you expected.
Python gives you several built-ins for this. They answer different questions, so do not treat their output as a complete description of an object.
The questions are worth separating. type() tells you what kind of value you have. dir() tells you which names might be available. getattr() reads a name when you only know it at runtime. help() points you toward the documented interface. These tools are most useful when you use the smallest one that answers the question in front of you.
dir(value) returns a sorted list of names that Python considers useful for that object. The list can include methods, attributes, and dunder names inherited from a class. It is a discovery tool, not a guarantee that every name can be called successfully.
my_list = [1, 2, 3]
names = [name for name in dir(my_list) if name in {"append", "pop"}]
print(names)The output is:
['append', 'pop']Use dir() when you know roughly what you are searching for. Reading the entire list for a large object can be noisy, so filtering it often makes the result easier to use.
The presence of a name does not prove that it is safe to call. A property may run code when read, and a method may need arguments. Treat dir() as a menu of possibilities, then inspect the signature or documentation before using an unfamiliar entry.
Many Python objects keep their instance attributes in a dictionary called __dict__. It is an attribute, not a function, so write person.__dict__, not person.__dict__().
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("John", 30)
print(person.__dict__)The output is:
{'name': 'John', 'age': 30}__dict__ shows attributes stored directly on this instance. A class can also have a __dict__, and some objects, including classes that use __slots__, do not expose an instance dictionary. If the attribute is missing, that does not mean the object has no state.
When the attribute name comes from a configuration value, use getattr() instead of constructing an expression:
class Settings:
timeout = 30
settings = Settings()
name = "timeout"
print(getattr(settings, name, 10))The third argument is a default for a missing attribute. hasattr() can answer whether a lookup succeeds, but remember that it may execute a property and can hide an exception raised during that lookup. For code you control, a direct access with a clear AttributeError is often easier to debug.
id(value) returns an integer that stays the same for that object while it exists. Two live objects cannot have the same identity value. The exact number is implementation-dependent, so it may change between runs.
Use the is operator when you want to test identity:
first = []
second = first
third = []
print(first is second)
print(first is third)
print(id(first) == id(second))The output is:
True
False
TrueDo not use id() to compare equal values. Two separate lists can contain the same items while representing different objects. Use == for value comparison and is for identity comparison.
help(value) opens Python's built-in documentation for a value. It can show a module, class, function, method, or object. In an interactive shell, Python may open the result in a pager, so press q when you are done reading.
help(str.upper)The result includes the method's description and signature when that information is available. help() is especially handy when you remember a method name but not its arguments.
callable(value) is another small check that helps during exploration. It tells you whether Python can call the value, while isinstance(value, SomeType) lets you check whether it fits a type or protocol you explicitly care about. Avoid turning a debugging tool into a maze of type checks; in ordinary Python code, trying the operation and handling a meaningful error can be clearer.
When an unfamiliar object appears in your code, start with type(value) to identify its class. Filter dir(value) to find possible names, inspect value.__dict__ when the object provides one, and use help() for the documentation. Use getattr() when the name is dynamic and id() only when the question is whether two names point to the same object.
My caveat is that introspection shows what Python exposes, not what the author intended. Treat the output as a clue, then read the class or docstring before changing code based on a guess. Printing an object's attributes can help you find a problem, but it is not a replacement for a documented interface or a test that explains the expected behavior.
