Python Language Fundamentals#
Python is the default language for most FAANG-style coding interviews. Before patterns and algorithms, you need a precise mental model of how Python treats values, names, and types — interviewers probe this constantly in warm-up questions and follow-ups.
How to use this page
Read it once end-to-end, then revisit the Interview traps section before mock interviews. Continue with Operators → Control Flow → Data Structures.
At a glance
| Track | Python Basics |
| Sections | 13 major topics |
| Outline | Use the right-hand TOC to jump |
Topics: What makes Python different · How Python runs your code · Variables: names bound to objects · Core built-in types (scalars) · Dynamic typing and type hints · Mutability vs immutability · Identity (is) vs equality (==) · Truthiness and falsy values · … (+5 more)
- What makes Python different
- How Python runs your code
- Variables: names bound to objects
- Core built-in types (scalars)
- Dynamic typing and type hints
- Mutability vs immutability
- Identity (
is) vs equality (==) - Truthiness and falsy values
- Naming conventions (PEP 8)
- Comments and docstrings
- Indentation and code blocks
- Literals and basic expressions … and 1 more sections
What makes Python different#
| Property | What it means in practice |
|---|---|
| Interpreted | Code runs via the Python interpreter; no separate compile step before execution (though bytecode is generated internally). |
| Dynamically typed | Variable names are not tied to a fixed type — the object carries the type. |
| Strongly typed | Python does not silently coerce incompatible types ("3" + 3 raises TypeError). |
| Indentation-based blocks | Whitespace defines scope — not braces. |
| Everything is an object | Integers, functions, classes, and modules are all objects with identity and type. |
Python prioritizes readability and expressiveness. In interviews, that translates to writing clear, idiomatic code quickly — not fighting the language.
How Python runs your code#
When you execute a .py file or REPL input:
- Source code is parsed into an abstract syntax tree (AST).
- The AST is compiled to bytecode (
.pyccached in__pycache__/when applicable). - The Python Virtual Machine (PVM) executes bytecode instruction by instruction.
python hello.py # run a script
python -m pdb hello.py # run with debugger
python -c "print(2+2)" # one-liner
You rarely need bytecode details in interviews, but knowing that Python is not a "compile-to-machine-code" language explains startup cost and why imports matter for performance.
Variables: names bound to objects#
In Python, a variable is a label (name) attached to an object in memory — not a typed box that holds a value.
x = 10 # name x → int object 10
x = "ten" # name x now → str object "ten" (the int may be garbage-collected)
Code & explanation
- Assignment never copies an object by default — it binds another name to the same object.
- Mutating through one name affects all names referencing that object.
- To get an independent container, use slicing (
[:]),.copy(), orcopy.deepcopy()for nested structures.
This model is the root cause of most "unexpected mutation" bugs and a common interview discussion topic.
Core built-in types (scalars)#
Before collections (Data Structures) and Strings, know the scalar types:
| Type | Example | Notes |
|---|---|---|
int |
42, -7, 0 |
Arbitrary precision — no fixed 32-bit overflow like C/Java. |
float |
3.14, 2.0, 1e-3 |
IEEE 754 double; beware precision issues. |
bool |
True, False |
Subclass of int (True == 1, False == 0). |
NoneType |
None |
Singleton meaning "no value" / "absent". |
str |
"hello", 'world' |
Immutable sequence of Unicode code points. |
type(42) # <class 'int'>
type(3.0) # <class 'float'>
type(True) # <class 'bool'>
type(None) # <class 'NoneType'>
# int does not overflow at 32 bits
big = 10 ** 100 # valid
Float precision trap#
0.1 + 0.2 == 0.3 # False
round(0.1 + 0.2, 10) # 0.3 — compare with tolerance in production
from decimal import Decimal
Decimal("0.1") + Decimal("0.2") == Decimal("0.3") # True
For money or exact decimal math, use decimal.Decimal or integer cents — not raw floats.
Dynamic typing and type hints#
Types live on objects, not variable names:
Type hints (PEP 484) document intent and enable static analysis — they do not enforce types at runtime:
def process(value: int) -> int:
return value * 2
process("hi") # still runs; mypy/pyright would flag it
Use hints in interview code when they clarify contracts — especially for function signatures and collection contents (list[int], dict[str, int]). Deep coverage: Function Annotations and Type Hints.
Mutability vs immutability#
| Immutable | Mutable |
|---|---|
int, float, bool, str, tuple, frozenset, bytes |
list, dict, set, bytearray, most user-defined objects |
Why interviewers care:
- Immutable objects can be dict keys and set elements (if hashable).
- Passing a mutable object to a function lets the callee modify caller state unless you copy.
tuplecontaining a mutable list is hashable as a whole only if all elements are hashable — the list inside can still mutate.
key = ([1, 2],) # tuple wrapping a list — hashable
cache = {key: "value"}
key[0].append(3) # mutates the list inside the tuple
# key is now ([1, 2, 3],) — still the same tuple object, but contents changed
See Data Structures for when to pick each collection type.
Identity (is) vs equality (==)#
| Operator | Compares | Use when |
|---|---|---|
== |
Values (calls __eq__) |
"Do these have the same content?" |
is |
Identity (same object in memory) | "Are these the exact same object?" — mainly None, True, False |
a = [1, 2, 3]
b = [1, 2, 3]
c = a
a == b # True — same contents
a is b # False — different list objects
a is c # True — c is an alias for a
x = None
if x is None: # preferred idiom
print("absent")
Interview trap
Never use is to compare strings or integers (except small cached ints like -5 to 256 in CPython). Always use == for value comparison.
# CPython interns small ints — do NOT rely on this in interviews
a = 256
b = 256
a is b # may be True or False depending on context — use ==
s1 = "hello"
s2 = "hello"
s1 is s2 # unpredictable — use ==
id(obj) returns the object's identity (memory address in CPython). Two objects with the same id are the same object.
Truthiness and falsy values#
Python evaluates values in boolean context (if, while, and, or, not):
Falsy: None, False, 0, 0.0, "", [], {}, set(), (), range(0)
Everything else is truthy.
def is_valid(name: str, items: list) -> bool:
if not name: # empty string → falsy
return False
if not items: # empty list → falsy
return False
return True
# Short-circuit evaluation
result = default or compute_expensive() # compute only if default is falsy
value = found if found is not None else fallback
and / or return the actual operand that determined the result, not strictly True/False:
Naming conventions (PEP 8)#
Consistent naming signals professionalism in live coding and take-home submissions:
| Style | Use for | Example |
|---|---|---|
snake_case |
Variables, functions, methods, modules | user_count, find_max() |
PascalCase |
Classes, exceptions | BinarySearchTree, ValueError |
SCREAMING_SNAKE_CASE |
Module-level constants | MAX_RETRIES = 3 |
_leading_underscore |
"Internal" / non-public by convention | _helper() |
__double_leading |
Name mangling in classes (avoid in basics) | __private |
MAX_PAGE_SIZE = 100
class UserService:
def __init__(self, db):
self._db = db # convention: internal attribute
def get_active_users(self):
return self._db.query("SELECT ...")
Rules of thumb:
- Be descriptive over terse:
left_indexbeatsl(except standard loopi,j). - Avoid shadowing builtins: never name a variable
list,dict,id,type,sum. - One statement per line; avoid semicolons except in one-liners on a whiteboard.
Comments and docstrings#
# Single-line comment — explain *why*, not *what* the syntax already shows
def binary_search(nums: list[int], target: int) -> int:
"""Return index of target in sorted nums, or -1 if absent.
Args:
nums: Sorted list of integers.
target: Value to locate.
Returns:
Zero-based index of target, or -1.
"""
lo, hi = 0, len(nums) - 1
while lo <= hi:
mid = (lo + hi) // 2
if nums[mid] == target:
return mid
if nums[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
#comments — for humans reading the code.- Docstrings — first string literal in a module, class, or function; accessible via
help()and.__doc__. - In interviews, a one-line docstring on non-trivial functions is enough; skip boilerplate for trivial helpers.
Indentation and code blocks#
Python uses 4 spaces per indentation level (never mix tabs and spaces). A colon (:) starts a new block:
def classify(score: int) -> str:
if score >= 90:
return "A"
elif score >= 80:
return "B"
else:
return "C"
for i in range(3):
print(i)
while stack:
node = stack.pop()
visit(node)
Blocks end when indentation returns to the previous level — there are no {}. This is non-negotiable in interviews: misaligned indentation is a syntax error.
Multi-line statements use parentheses, brackets, or backslashes — prefer implicit line joining inside ():
Full branching and looping patterns: Control Flow.
Literals and basic expressions#
# Numbers
decimal = 42
binary = 0b101010
hexadecimal = 0x2A
floating = 3.14
scientific = 1.5e6
# Strings (see Strings.md for formatting, slicing, methods)
single = 'hello'
double = "hello"
multi = """Line one
Line two"""
# Collections (see Data Structures.md)
nums = [1, 2, 3]
coords = (10, 20)
config = {"debug": True, "retries": 3}
tags = {"python", "faang"}
Operator precedence and behavior (//, %, **, bitwise, comparisons): Operators.
The if __name__ == "__main__" pattern#
Every Python file is a module. When executed directly, its __name__ is "__main__"; when imported, __name__ is the module path.
This keeps reusable functions importable without side effects. Environment setup (venv, pip, running tests): The Python Environment.
Interview traps (quick reference)#
| Trap | What goes wrong | Safe approach |
|---|---|---|
| Shared mutable default | def f(x=[]): — list reused across calls |
Use None and create inside: if x is None: x = [] |
| Shallow vs deep copy | Nested list mutated through copy | copy.deepcopy() when needed |
is vs == |
Wrong identity checks on values | == for values; is for None |
| Float equality | 0.1 + 0.2 != 0.3 |
Compare with epsilon or use Decimal |
Truthiness on 0 |
if count: skips zero |
if count is not None: or if count > 0: when zero is valid |
| Shadowing builtins | list = [1,2,3] breaks list() |
Never reuse builtin names |
Code & explanation — mutable default argument
- Default arguments are evaluated once at function definition time.
- A mutable default is shared across all calls that omit the argument.
- The
Nonesentinel pattern creates a fresh object per call.
Details on functions, scope, and closures: Functions and Code Reuse and Closures.
Mental model checklist#
Before moving on, you should be able to answer:
- What happens in memory when you write
b = afor a list? - Why is
Nonechecked withisinstead of==? - Which types can be dictionary keys?
- What values are falsy in Python?
- Does
type(x)change when you reassignxto a different kind of object?
If any of these are shaky, reread the relevant section above.
What's next#
| Topic | Page |
|---|---|
| Arithmetic, comparison, logical operators | Operators |
if / for / while / match |
Control Flow |
| Lists, tuples, dicts, sets | Data Structures |
| Slicing, formatting, string methods | Strings |
| Functions, parameters, scope | Functions and Code Reuse |
try / except / raising errors |
Basic Error Handling |
| Interview algorithms & patterns | Programming |