Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Python  Module 18 – Advanced Python: data model, metaprogramming and internals

Introspection: inspect, vars() and type()

Inspect objects at run time with type(), vars(), dir() and the inspect module: read signatures and source code, and list the functions a module defines.

  • Advanced
  • 20 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free core, Pro depth: 8 of 20 minutes free
Free: type(), vars() and dir() answer different questions · A signature lists every parameter with its kind and default · getmembers lists a module's contents, imports included · 2 more sections. With a pass: 2 in-depth parts and 3 interview answers, about 12 minutes.

What you will learn

  • Inspect objects, classes and modules at run time with type(), vars(), dir() and getattr()
  • Read a function's parameters, kinds, defaults and annotations with inspect.signature
  • Retrieve source code with inspect.getsource, and handle the errors it raises
  • Decide when introspection helps and when explicit registration is clearer

Before you start

A command-line tool can print the help for deploy(), every option with its kind and its default, without anyone typing the options by hand: it asks the function for its parameters while the program runs. That is introspection, a program examining its own objects: what type a value has, which attributes it holds, which parameters a function takes and where its source code lives.

Python answers the everyday questions with built-in functions: type() and isinstance() for the class, vars() for an object’s own attributes, dir() for every name it answers to, getattr() and hasattr() for one attribute at a time, and callable() for whether something can be called. The inspect module goes further: inspect.signature() describes how a function is called, inspect.getmembers() lists what a module or class contains, and inspect.getsource() returns source text.

type(), vars() and dir() answer different questions

everyday_tools.py asks one ticket, its class and a module the everyday questions:

One ticket, examined with the built-in functions Python · everyday_tools.py
import json


class Ticket:
    fare = 450

    def __init__(self, passenger, seat):
        self.passenger = passenger
        self.seat = seat

    def label(self):
        return f"{self.passenger} {self.seat}"


t = Ticket("Asha", "B2-31")
print(type(t).__name__, type(t) is Ticket, isinstance(t, object), type(Ticket).__name__)
print(vars(t))                                                # the instance's own attributes
print([n for n in vars(Ticket) if not n.startswith("__")])    # what the class body defined
print([n for n in dir(t) if not n.startswith("__")])          # all of it, sorted, inherited names too
print(getattr(t, "seat"), getattr(t, "coach", "no coach"), hasattr(t, "label"), callable(t.label))
print(type(json).__name__, "dumps" in vars(json), len(dir(t)) > len(vars(t)) + len(vars(Ticket)))


class Point:
    __slots__ = ("x", "y")


try:
    vars(Point())
except TypeError as e:
    print("TypeError:", e)

Output

Ticket True True type
{'passenger': 'Asha', 'seat': 'B2-31'}
['fare', 'label']
['fare', 'label', 'passenger', 'seat']
B2-31 no coach True True
module True True
TypeError: vars() argument must have __dict__ attribute

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 everyday_tools.py

  • type(t) is the class itself, and the class’s own type is type. isinstance() is the better test in program logic, because it accepts subclasses too.
  • vars(t) is the instance’s own attribute dictionary, and vars(Ticket) holds what the class body defined. dir() merges the names of the instance, its class and every base into one sorted list, which is why it is longer than both together: the extra names come from object.
  • getattr() with a default answers “this attribute, or that value”, and hasattr() and callable() answer yes or no. A module is an object like any other: type(json) is module, and vars(json) is its namespace.
  • An object without a __dict__, such as an instance of a class with __slots__, has no vars().

For the ticket t, the names come from three places, and vars() shows one of them at a time:

dir(t) gathers the names of the instance, its class and the bases; vars() returns only one of those dictionaries.dir(t): every name t answers to, sortedvars(t): t's own attributespassenger, seatvars(Ticket): what the class body definedfare, label, __init__the bases' names, from object__repr__, __eq__, __class__ and morethenthen

What dir() and vars() show for an instance t of the class Ticket

Text description of the diagram

The diagram is one large box labelled "dir(t): every name t answers to, sorted", holding three smaller boxes in a column.

  • The first is vars(t), the instance's own attributes: passenger and seat.
  • The second is vars(Ticket), what the class body defined: fare, label and __init__.
  • The third holds the names that come from the bases, here only object: __repr__, __eq__, __class__ and many more.

Arrows marked "then" lead from each small box to the next, in the order attribute lookup searches them. dir() merges the names of all three into one sorted list, while vars() returns a single one of the dictionaries, with values.

The table sums up what each tool answers, and the catch to keep in mind with each:

Tool What it answers, and what to keep in mind
type(obj), isinstance(obj, C) What class it is; isinstance accepts subclasses, type(obj) is C does not
vars(obj) What it stores itself; TypeError for objects without a __dict__
dir(obj) Which names it answers to; meant for exploring, and its exact contents may change between versions
hasattr(), getattr() Whether it has one attribute; both read the attribute, so a property’s code runs
callable(obj) Whether it can be called; classes are callable, and so are objects with __call__

Checkpoint

Check: type(), vars() and dir()

  1. Question 1 of 2 What does vars(t) give for the ticket t = Ticket("Asha", "B2-31") of everyday_tools.py?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: {'passenger': 'Asha', 'seat': 'B2-31'}

    vars(obj) returns obj.__dict__: for an instance, what it stores itself. The class's attributes are in vars(Ticket), and dir(t) merges the names of both and of the bases.

  2. Question 2 of 2 Why is len(dir(t)) larger than len(vars(t)) + len(vars(Ticket))?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: dir() also lists the names t answers to through object, the base of every class

    The diagram shows it: vars(t) and vars(Ticket) are two of the dictionaries dir(t) draws on, and the names from object make up the rest, which is why the last check of the example printed True.

A signature lists every parameter with its kind and default

cli_help.py writes the help of two commands from nothing but their signatures and docstrings:

Help text for two commands, generated from their signatures Python · cli_help.py
import inspect


def deploy(service: str, /, version: str, *, region: str = "ap-south-1", dry_run: bool = False) -> None:
    """Deploy a service to one region."""


def rollback(service: str, steps: int = 1) -> None:
    """Roll a service back by a number of releases."""


def help_for(func):
    """Usage text built from the function's signature and docstring."""
    sig = inspect.signature(func)
    lines = [f"{func.__name__}{sig}", f"    {inspect.getdoc(func)}"]
    for p in sig.parameters.values():
        annotation = "" if p.annotation is p.empty else p.annotation.__name__
        default = "required" if p.default is p.empty else f"default {p.default!r}"
        lines.append(f"    {p.name:8} {annotation:5} {p.kind.description:22} {default}")
    return "\n".join(lines)


for command in (deploy, rollback):
    print(help_for(command))

Output

deploy(service: str, /, version: str, *, region: str = 'ap-south-1', dry_run: bool = False) -> None
    Deploy a service to one region.
    service  str   positional-only        required
    version  str   positional or keyword  required
    region   str   keyword-only           default 'ap-south-1'
    dry_run  bool  keyword-only           default False
rollback(service: str, steps: int = 1) -> None
    Roll a service back by a number of releases.
    service  str   positional or keyword  required
    steps    int   positional or keyword  default 1

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 cli_help.py

inspect.signature(func) returns the function’s signature: A function's parameters with their kinds and defaults, as inspect.signature() and help() show them, such as (text, /, minutes, *, repeat=False, sound='chime'). More in “Parameters: defaults, keyword-only, positional-only” as a Signature object, whose parameters map each name to a Parameter with a kind, a default and an annotation; a missing default or annotation is the marker Parameter.empty, never None, so that a real default of None stays visible. Printing the signature reproduces the parameter list, / and * included. The kinds follow those markers: parameters before / are positional-only parameter: A parameter before / in a signature, such as obj in len(obj, /). A call passes it by position only, never by its name. More in “Parameters: defaults, keyword-only, positional-only”, those after * are keyword-only parameter: A parameter after * or *args in a signature, such as key in sorted(iterable, /, *, key=None, reverse=False). A call must pass it by name. More in “Parameters: defaults, keyword-only, positional-only”, and the rest can be passed either way. The help text needed no list of options typed by hand, so it cannot fall out of step with the functions. signature() also accepts classes, bound methods, functools.partial objects and many built-ins, and on Python 3.14 it can return annotations as the text written in the source, without evaluating them.

Checkpoint

Check: Reading a signature

  1. Question 1 of 2 How does help_for() in cli_help.py tell a required parameter from one with a default?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: It compares p.default with p.empty, the marker for no default

    default = "required" if p.default is p.empty else ... is the test. Parameter.empty, never None, marks a missing default or annotation.

  2. Question 2 of 2 In deploy(service: str, /, version: str, *, region: str = "ap-south-1", dry_run: bool = False), what kind is version?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: positional or keyword

    The help text printed version as positional or keyword: the markers / and * divide the list, and a parameter between them can be passed by position or by name.

getmembers lists a module’s contents, imports included

main.py lists the functions of billing.py, then narrows the list to the ones the module defines itself:

The public functions of a module, without what it imported

Python (main.py) · members/main.py

import inspect
import json

import billing

functions = inspect.getmembers(billing, inspect.isfunction)    # (name, value) pairs, sorted by name
print([name for name, _ in functions])

public = [
    name
    for name, func in functions
    if not name.startswith("_") and inspect.getmodule(func) is billing
]
print(public)
print([name for name, _ in inspect.getmembers(billing, inspect.isclass)])
print(inspect.ismodule(billing), inspect.ispackage(billing), inspect.ispackage(json))
print(inspect.getdoc(billing.total), "|", billing.invoice_text("Asha", 100))

Output

['_round_up', 'dedent', 'invoice_text', 'total']
['invoice_text', 'total']
['Invoice']
True False True
The amount with tax. | Asha: 118.0

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 main.py

Python (billing.py) · members/billing.py

"""Billing helpers."""
from math import ceil                # a built-in function: not a Python function at all
from textwrap import dedent          # a Python function, but textwrap's, not ours

TAX_RATE = 0.18


def total(amount):
    """The amount with tax."""
    return round(amount * (1 + TAX_RATE), 2)


def invoice_text(name, amount):
    """A short invoice, as text."""
    return dedent(f"""
        {name}: {total(amount)}
    """).strip()


def _round_up(amount):               # private by convention
    return ceil(amount)


class Invoice:
    """An invoice record."""

Output

It printed nothing.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 billing.py

inspect.getmembers(billing, inspect.isfunction) returns (name, value) pairs sorted by name, reading each attribute to get its value. Step through the names it reads, in sorted order, and the three tests that narrow them to the public functions:

Interactive · stage by stage From every name in billing to its public functions Step through the names that dir(billing) lists, in its sorted order. Each stage is one test; the lit stage is the one that decides the name.
From every name in billing to its public functions, step 7 of 7. Showing A name of billing (total), inspect.isfunction? (yes), Defined in billing? (yes: billing), A public name? (yes), Kept (invoice_text, total). In focus: Kept.A name of billingtotalinspect.isfunction?yesDefined in billing?yes: billingA public name?yesKeptinvoice_text, total

The drawing shows the whole scene; its 7 steps are listed below.

Every step as text
  1. Invoice. getmembers() reads every name that dir(billing) lists. Invoice is a class, so inspect.isfunction says no; inspect.isclass would have kept it.
  2. TAX_RATE and the dunders. TAX_RATE is a float, and the names with two underscores on each side, such as __name__ and __file__, are not functions either.
  3. _round_up. _round_up is a Python function defined in billing, so getmembers() keeps it, but the leading underscore marks it private, so the public list drops it.
  4. ceil. ceil is a built-in function, written in C. inspect.isfunction is true only for functions written in Python, so getmembers() leaves it out.
  5. dedent. dedent is a Python function, so getmembers() keeps it, although billing only imported it. inspect.getmodule() looks up where it was defined, textwrap, and the test fails.
  6. invoice_text. A Python function, defined in billing, with a public name, so it passes every test.
  7. total. total passes in the same way. The public list ends as invoice_text and total, which is what the second line of output shows.

The list includes dedent, a Python function that billing merely imported, and leaves out ceil, which is a built-in function, not a Python one. inspect.getmodule() tells them apart by where each function was defined, and the leading underscore excludes the private helper. The other predicates work the same way, isclass here, and inspect.ispackage(), new in Python 3.14, says whether a module is a package.

Checkpoint

Check: Listing what a module defines

  1. Question 1 of 2 Why does inspect.getmembers(billing, inspect.isfunction) list dedent?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: billing imported it, so it is an attribute of billing, and it is a Python function

    An imported function is a member of the module that imports it. inspect.getmodule(func) is billing tells the functions billing defines from those it imported, which is how the public list dropped dedent.

  2. Question 2 of 2 billing.py imports ceil from math. Why is ceil missing from what getmembers(billing, inspect.isfunction) returns?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It is a built-in function written in C, and inspect.isfunction is true only for Python functions

    getmembers() lists every attribute for which the predicate is true. isfunction means a Python function, so a built-in written in C such as ceil fails it, while dedent, written in Python, passes.

Source code comes from the file, so some objects have none

source_code.py writes a module at run time and reads a function’s source back, then asks for two that have none:

The source of a function in a new module, and two that have none Python · source_code.py
import importlib
import inspect
from pathlib import Path

# Write a small module next to this file, as an editor would, then import it.
Path("rules.py").write_text(
    "def discount(total):\n"
    '    """Ten per cent off orders above 1,000."""\n'
    "    return total * 0.9 if total > 1000 else total\n"
)
importlib.invalidate_caches()        # let the import system notice a file created after start-up
import rules

print(inspect.getsource(rules.discount), end="")
print(Path(inspect.getsourcefile(rules.discount)).name, inspect.getsourcelines(rules.discount)[1])

namespace = {}
exec("def made_at_run_time(x):\n    return x * 2\n", namespace)
for obj in (len, namespace["made_at_run_time"]):
    try:
        inspect.getsource(obj)
    except (TypeError, OSError) as e:
        print(f"{type(e).__name__}: {e}")

Output

def discount(total):
    """Ten per cent off orders above 1,000."""
    return total * 0.9 if total > 1000 else total
rules.py 1
TypeError: module, class, method, function, traceback, frame, or code object was expected, got builtin_function_or_method
OSError: could not get source code

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 source_code.py

getsource() reads the file a function came from, using the code object’s file name and first line number, so it works for any Python code whose source file still exists. The example writes rules.py at run time and calls importlib.invalidate_caches() first, because the import system caches directory listings and may otherwise miss a file created after start-up. Follow the three objects through what getsource() needs:

Interactive · stage by stage Where getsource() finds the text, and where it cannot Step through the three objects of source_code.py. Each stage is one thing getsource() needs; the run stops at the first one that is missing.
Where getsource() finds the text, and where it cannot, step 4 of 4. Showing getsource(obj) (made_at_run_time), Its code object (file name <string>), That file is read (no such file), The block is returned (not reached). In focus: That file is read.getsource(obj)made_at_run_timeIts code objectfile name <string>That file is readno such fileThe block is returnednot reachedOSError: could not get source code

The drawing shows the whole scene; its 4 steps are listed below.

Every step as text
  1. rules.discount. discount is a Python function. Its code object records the file it came from, rules.py, and its first line, line 1.
  2. The file is read. getsource() reads rules.py and returns the block that starts at line 1, the three lines of discount, which the program printed.
  3. len. len is a built-in function, written in C. It has no Python code object, so getsource() stops at the first need with TypeError.
  4. Code made by exec(). made_at_run_time was compiled by exec() from a string, so its code object names no file that exists, and getsource() raises OSError.

A built-in function has no Python source, so getsource(len) raises TypeError, and code created with exec() has no file, so asking for its source raises OSError.

A few more tools of the module handle the harder cases. inspect.unwrap() follows the __wrapped__ chain that functools.wraps leaves on a decorated function, and signature() follows it too. Signature.bind() checks a call against a signature before the call is made. inspect.getattr_static() reads an attribute without running a property or __getattr__. And inspect.currentframe() and inspect.stack() show the chain of calls that led to the current line.

Checkpoint

Check: Where source code comes from

  1. Question 1 of 2 What does inspect.getsource() use to find a function's text?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: The file name and first line recorded in its code object, and that file still being there

    getsource() follows the code object to its file and first line and returns the block found there, which is why it works for any Python code whose source file still exists, and only for that.

  2. Question 2 of 2 A function was created with exec() from a string. What does inspect.getsource() do with it?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: It raises OSError, because the code has no source file to read

    The run printed OSError: could not get source code for made_at_run_time, and a TypeError for len, which has no Python code object at all.

Python Online Compiler Run help_for() from the signature example on sorted, json.dumps and max, and see which one has no signature.

Practise: types, signatures, members and source

Practice asks about the four ideas above, weakest first, and tells you at once why each answer is right or not.

Practice that adapts to you

Practise this lesson

9 questions about 12 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 9 t is an instance of Ticket. Which statement about vars(t) and dir(t) is true?

    Choose one answer.

    Show the answer: Question 1 of 9

    Answer: vars(t) is t's own attribute dictionary; dir(t) is a sorted list of names from t, its class and the bases

    vars() returns the __dict__ of the object you pass, with values. dir() merges the names that the instance, its class and every base answer to, sorted, which is why it is longer and holds no values.

  2. Question 2 of 9 class Express(Ticket): pass and e = Express("Ravi", "A1-02"). What are type(e) is Ticket and isinstance(e, Ticket)?

    Choose one answer.

    Show the answer: Question 2 of 9

    Answer: False and True

    This is why isinstance() is the better test in program logic: code that accepts a Ticket should accept an Express too. type(obj) is C is for the rare case that must exclude subclasses.

  3. Question 3 of 9 A debugging helper prints vars(obj) for every object it is given, and fails on instances of a class with __slots__. What can it use for those objects instead?

    Choose one answer.

    Show the answer: Question 3 of 9

    Answer: dir(obj) for the names, then getattr(obj, name) for each value

    vars() needs a __dict__, and an instance of a class with __slots__ has none. Its names are still reachable: dir() lists them and getattr() reads them, at the cost of also listing everything the object inherits.

  4. Question 4 of 9 What does this print?

    Read the code, then choose one answer.

    import inspect
    
    def send(to, /, body, *, urgent=False): ...
    
    sig = inspect.signature(send)
    print(sig.parameters["to"].kind.description, sig.parameters["urgent"].default)
    Show the answer: Question 4 of 9

    Answer: positional-only False

    to comes before /, so it is positional-only. urgent comes after *, so it is keyword-only, and its default is the False written in the definition; Parameter.empty marks only parameters without a default.

  5. Question 5 of 9 A help generator writes "required" if not p.default else f"default {p.default!r}" instead of comparing with p.empty. Which parameters of deploy(service, /, version, *, region="ap-south-1", dry_run=False) does it label wrongly?

    Choose one answer.

    Show the answer: Question 5 of 9

    Answer: service, version and dry_run

    The marker Parameter.empty exists so that no real default can be mistaken for "none": compare with p.default is p.empty. A truth test mixes the marker up with real defaults that happen to be false, such as False, 0, '' or None.

  6. Question 6 of 9 inspect.getmembers(billing, inspect.isfunction) also lists dedent, which billing imports from textwrap. How do you keep only the functions that billing itself defines?

    Choose one answer.

    Show the answer: Question 6 of 9

    Answer: Keep those for which inspect.getmodule(func) is billing

    An imported function is a member of the module that imports it, so dir() and callable() cannot tell the two apart. getmodule() finds where the function was defined, here textwrap, from the function's own __module__.

  7. Question 7 of 9 Which call lists the classes a module holds, as (name, class) pairs?

    Choose one answer.

    Show the answer: Question 7 of 9

    Answer: inspect.getmembers(module, inspect.isclass)

    The predicates of inspect work the same way: getmembers() reads every attribute and keeps those for which the predicate is true. The example's third line printed ['Invoice'].

  8. Question 8 of 9 What does inspect.getsource(len) do?

    Choose one answer.

    Show the answer: Question 8 of 9

    Answer: It raises TypeError, because len is a built-in function with no Python source

    getsource() needs a module, class, method, function, traceback, frame or code object written in Python. Built-ins are written in C, so it raises TypeError; for Python code whose file is missing, such as code made with exec(), it raises OSError instead.

  9. Question 9 of 9 A test writes plugin.py at run time and imports it at once, and the import sometimes fails with ModuleNotFoundError although the file is there. What do you call before the import?

    Choose one answer.

    Show the answer: Question 9 of 9

    Answer: importlib.invalidate_caches()

    The import system may miss a file created after start-up because it caches what each folder holds. importlib.invalidate_caches() clears those caches, which is why the example calls it before import rules.

Signatures in depth: decorators, checked calls and annotations read as text Pro: opens with a Pro, Premium or Ultimate pass

Tools that build on signatures break in three predictable places: decorators, bad calls and annotations that cannot be evaluated yet. This part reproduces each one, fixes it with the standard tools, and ends with an exercise.

In this part:

  • What a decorator does to a signature, and how wraps restores it
  • Signatures of classes, methods, partials and built-ins
  • Checking a call before making it, with Signature.bind
  • Annotations read as text, without running them
  • Exercise: describe a function without evaluating its annotations
    • Describe a function's parameters without evaluating its annotations

1 diagram · 4 runnable examples (Python) · 1 exercise with sample tests · about 9 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Introspection without side effects: properties that fire, getmembers counted, and when to stop Pro: opens with a Pro, Premium or Ultimate pass

Reading an attribute can run code, so careless introspection opens connections, computes reports or raises in the middle of a health check. This part counts what hasattr() and getmembers() really execute, shows the static alternatives, and weighs introspection against explicit registration.

In this part:

  • hasattr() runs code: a property firing, reproduced
  • What getmembers() reads, counted, and what its static version skips
  • Attributes that getmembers() cannot see
  • Who called this function: frames, and the reference cycle they create
  • Introspection or explicit registration: choosing

1 diagram · 4 runnable examples (Python) · about 10 minutes

Opening this part with your pass…Pro part. It opens with the same pass as the part above.

Interview questions

  1. Fresher · Warm-up

    What is the difference between dir() and vars()?

    vars(obj) returns the object's own attribute dictionary, its __dict__, with the values: for an instance, the attributes set on it; for a class, what its body defined; for a module, its namespace. It raises TypeError for objects without a __dict__, such as instances of classes with __slots__. dir(obj) returns a sorted list of names only, gathered from the object, its class and all the bases, so it includes inherited methods and the names that come from object; a class can change what it reports with __dir__. Use vars() to see or copy an object's own state, and dir() to explore what an object offers, at the interactive prompt or in tab completion. The documentation describes dir() as a convenience whose exact contents may change between versions, so program logic should not depend on it.

  2. Mid to senior

    Your web framework builds request validators from handler signatures with inspect.signature(). After someone adds a timing decorator to every handler, all handlers accept any arguments and validation silently stops. Diagnose the bug, and make the framework robust against it.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    A health-check endpoint calls hasattr(client, 'session') on 40 client objects. After a library upgrade its response time jumps from 5 ms to 2 s, and sometimes it raises ConnectionError. Explain what is happening and how you would check for the attribute without side effects.

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    A rules feature shows users the source of their rule functions with inspect.getsource(). It works on developers' machines but raises OSError in the deployed app. Give the likely causes and the alternatives you would consider.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • type() and isinstance() answer what class an object has; vars() gives its own attributes; dir() lists every name it answers to.
  • inspect.signature() describes parameters by name, kind, default and annotation; Parameter.empty marks what is missing.
  • inspect.getmembers() with a predicate lists a module’s or class’s members; inspect.getmodule() separates what a module defines from what it imports.
  • inspect.getsource() needs a Python source file: built-ins raise TypeError and code without a file raises OSError.

Terms in this lesson

introspection
A program examining its own objects while it runs: their type, their attributes, a function's parameters or its source code, with built-ins such as type(), vars() and dir() and with the inspect module.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.