Interpreter Execution Model
What the interpreter does with your code: compiles it into code objects, runs it in frames you can read, then tears the process down in order. Interviewers reach here when behaviour defies the source.
part ofPythonoverview, primer and where to startread it →on this pageshowhide
explore
- Compilation Pipeline16 questions
- Code Objects4 questions
- Bytecode Cache Invalidation4 questions
- AST Parsing and Rewriting4 questions
- exec and eval Namespaces4 questions
- Live Call Stack7 questions
- Frame Objects3 questions
- Recursion Depth Limits4 questions
- Reflection Over Objects12 questions
- Signature Binding4 questions
- Dynamic Attribute Access4 questions
- Function Object Attributes4 questions
- Invocation and Environment22 questions
- Isolated and Safe Modes3 questions
- Development Mode Checks4 questions
- Stream Buffering and Encoding4 questions
- Hash Randomization4 questions
- Optimization Flags and __debug__4 questions
- Version and Implementation Checks3 questions
- Process Lifetime20 questions
- Site and .pth Files4 questions
- Startup Import Cost4 questions
- Teardown and atexit4 questions
- Exit Codes and SystemExit4 questions
- Module Entry and __main__4 questions
questions
77 · 5 sectionsWhat is the difference between Python's eval() and exec()?
basics
~10 seval() evaluates one expression and returns its value. exec() runs statements - assignments, loops, imports, definitions - and always returns None. Hand eval() a statement and it raises SyntaxError.
What is Python's __pycache__ directory and when does CPython reuse a .pyc file?
basics
~20 sCPython caches each module's compiled bytecode in a pycache directory next to the source. It reuses that .pyc only when the magic number matches the interpreter and the recorded source timestamp and size still match.
What is the difference between ast.NodeVisitor and ast.NodeTransformer in Python?
basics
~10 sast.NodeVisitor reads a tree: its visit_<NodeType> methods return whatever they like and the tree is untouched. ast.NodeTransformer rewrites it: whatever a visit method returns replaces the node, and returning None deletes it.
Why does a Python code-checking tool use ast.parse() instead of regular expressions over the source?
basics
~20 sast.parse() turns source text into a tree that mirrors Python's own grammar, so a tool matches real structure - a call, an assignment, a function - instead of characters. Regexes cannot see nesting, strings or comments.
Which CPython limit decides when deep recursion raises RecursionError?
basics
~10 sCPython counts how many Python frames are stacked on the current thread and raises RecursionError once that count passes the interpreter's recursion limit, which is 1000 by default. sys.getrecursionlimit() reads it; sys.setrecursionlimit() changes it.
How can a Python function find out which function called it, using frame objects?
basics
~10 sGet a frame object with inspect.currentframe() or sys._getframe(), follow its f_back attribute to the caller's frame, then read f_code.co_name for the caller's name and f_lineno for the call site.
Why does assigning into locals() inside a Python function not change the variable?
basics
~20 sFunction locals live in numbered slots on the frame, not in a dictionary. locals() manufactures a snapshot dict of those slots, so writing into it changes only the copy. Since 3.13, a frame's f_locals is a write-through proxy and does update the real variable.
Why can raising sys.setrecursionlimit crash the process instead of curing RecursionError?
basics
~20 ssys.setrecursionlimit only moves CPython's frame counter. The real constraint is the fixed stack the operating system gave the thread, so lifting the counter past what that stack supports turns a catchable RecursionError into a hard crash with no traceback.
Why does calling inspect.stack() in a per-document logging helper blow a rebuilder's latency budget?
basics
~20 sinspect.stack() walks every frame to the top of the stack, builds a record for each, and by default reads a source line per frame through the line cache. Cost scales with stack depth and touches the filesystem, so it is thousands of times slower than sys._getframe(1).
What does getattr(obj, 'x', None) give you that plain obj.x does not?
basics
~20 sgetattr takes the attribute name as a runtime string, and its optional third argument is returned instead of raising AttributeError when the name is absent. Dotted syntax needs the name written in the source and always raises.
Why does a decorated function report the wrapper's __name__ and __doc__, and how does functools.wraps fix it?
basics
~20 sDecoration rebinds the name to the wrapper, a different function object with its own metadata, so name, qualname and doc are the wrapper's. Applying functools.wraps to the wrapper copies that metadata from the wrapped function and sets wrapped.
What do a Python function's __defaults__ and __kwdefaults__ hold, and when are they evaluated?
basics
~20 sdefaults is a tuple of default values for the trailing positional-or-keyword parameters, or None when there are none. kwdefaults is a dict of defaults for keyword-only parameters. Both are evaluated once when the def executes and stored on the function object.
What are the five inspect.Parameter kinds, and what does each allow at a call site?
basics
~20 sinspect.Parameter has five kinds: POSITIONAL_ONLY, POSITIONAL_OR_KEYWORD, VAR_POSITIONAL, KEYWORD_ONLY and VAR_KEYWORD. The kind says how an argument may be supplied — by position, by either, by name only, or swept into a catch-all — and .parameters lists them in that order.
What does a Python function's __name__ hold, and how does __qualname__ differ from it?
basics
~20 sname is the bare name from the def statement. qualname is the dotted path to the definition inside its module, including enclosing classes and functions, with <locals> marking a function-local definition. module names the defining module.
Why does a Python set of strings iterate in a different order on each run?
basics
~20 sCPython salts str and bytes hashes with a seed picked at interpreter start-up, so the same strings land in different slots of the set's hash table each run. Set iteration follows slot order, so the order changes.
Why does a local file named logging.py shadow the standard library's logging module?
basics
~10 sThe interpreter prepends the running script's own directory to sys.path, so import searches it before the standard library and takes the first match. Run with -P, or set PYTHONSAFEPATH=1, to drop that prepended entry.
What does Python's -O flag actually do to assert statements and __debug__?
basics
~20 sRunning the interpreter with -O compiles away every assert statement and sets the built-in constant debug to False, so if debug blocks vanish too. It performs no other optimization and does not make code faster.
Why compare sys.version_info as a tuple instead of parsing the sys.version string?
basics
~20 ssys.version_info is a five-field named tuple, so comparing it against a tuple such as (3, 11) is an element-wise numeric comparison. sys.version is a free-form display string, and text comparison wrongly orders 3.9 after 3.14.
What does CPython's `-X dev` development mode switch on at runtime?
basics
~10 sRunning python -X dev (or setting PYTHONDEVMODE=1) enables the default warning filter so DeprecationWarning and ResourceWarning appear, debug hooks in the memory allocator, faulthandler, asyncio debug mode, and stricter encoding-argument checks.
What does sys.exit() do, and what process exit code results?
basics
~20 ssys.exit() raises SystemExit rather than killing the process outright; if nothing catches it, the interpreter shuts down. No argument or None gives exit status 0, an integer gives that value, and any other object is printed to stderr with status 1.
What does sys.argv hold when you run python tools/report.py versus python -m tools.report?
basics
~10 ssys.argv[0] is the program name: the script path exactly as typed, or, under -m, the full path of the resolved module file. The program's own options always start at sys.argv[1]; interpreter switches never appear.
Why does `except Exception` not catch SystemExit from sys.exit()?
basics
~20 sSystemExit inherits directly from BaseException, not from Exception, so except Exception deliberately lets it pass. That keeps a broad error handler from swallowing a requested shutdown. A bare except: means except BaseException and does swallow it.
Why move an `import` statement into a function body instead of the module top?
basics
~20 sTo keep its cost off the startup path. A module-level import runs while the module body runs, so every process pays it; an import inside a function runs only when that function is called, and never if it is not. It also breaks import cycles.
How does python -m pkg.mod differ from running python pkg/mod.py?
basics
~20 sPython's -m switch resolves a dotted module name through the import system, imports the parent package, and puts the working directory first on sys.path. A file path runs that file with its own directory first and no package context, so relative imports fail.