Python is the working language for much of AI engineering. You will use it for data preparation, evaluation scripts, model-serving code, RAG pipelines, and the small tools that connect systems together.
This chapter covers the Python features that show up repeatedly in AI codebases: collections, comprehensions, unpacking, formatting, slicing, built-in iteration tools, text handling, and a few everyday idioms. The goal is not to memorize every Python feature. It is to build habits that keep your code clear when you are working with real data and external APIs.
Python's core collection types are simple, but choosing the right one matters. A good choice means fewer conversions, fewer hidden assumptions about ordering, and fewer bugs around mutation.
Here is how they compare:
Here is how each one commonly appears in AI projects.
Lists are ordered, mutable sequences. In AI code, lists hold everything from raw token sequences to batches of embeddings.
Dicts are key-value stores. They are everywhere in AI code: model settings, API payloads, token vocabularies, lookup tables, and parsed JSON responses.
Sets are unordered collections of unique elements. Average-case membership checks are constant time, which matters when you filter thousands of tokens or document IDs.
If you need both uniqueness and order, a common pattern is list(dict.fromkeys(items)). This preserves insertion order while removing duplicates.
Tuples are like lists but immutable. You cannot change them after creation. This makes them useful as dict keys (lists cannot be dict keys because they are mutable) and as return values from functions.
Returning multiple values and unpacking them at the call site is routine Python. Use it for small, tightly related results. Once the return value has more structure or survives beyond a few lines, prefer a dataclass or Pydantic model with named fields.
Comprehensions let you transform or filter a collection in a single expression. In AI work, they are common in preprocessing, feature extraction, evaluation summaries, and batch assembly.
The basic pattern is [expression for item in iterable if condition].
Same idea, but the result is a dictionary. This is useful for building lookup tables and inverting mappings.
Set comprehensions are useful when you want unique values from a collection.
Do not turn comprehensions into puzzles. A single filter or transform is usually clear. A double-nested comprehension can be fine when it reads naturally. If you need three levels, side effects, or exception handling, use a regular loop. Preprocessing code should be easy to review.
Unpacking appears everywhere in Python AI code: evaluation loops, dataset iteration, metric returns, and batch processing.
The * operator captures "the rest" into a list.
Python functions commonly return tuples, and callers unpack them directly. This pattern is common in training loops, evaluation code, and small utility functions.
F-strings, short for formatted string literals, are the standard way to interpolate values into strings in modern Python.
Older approaches like % formatting and .format() still work and appear in older code. For new application code, f-strings are usually clearer and easier to scan.
Slicing extracts portions of lists, strings, arrays, and tensors. The syntax is sequence[start:stop:step], where start is inclusive and stop is exclusive.
When you work with NumPy arrays and PyTorch tensors, slicing becomes essential. The same basic syntax carries over:
You do not need to fully understand NumPy yet. The slicing syntax you learn here transfers directly to the numerical computing libraries you will use later.
Strings support the same slicing syntax. This is useful for truncating prompts, extracting prefixes, or working with fixed-format text.
:=The walrus operator (:=), introduced in Python 3.8, assigns a value as part of an expression.
Use it sparingly. It is helpful when it removes duplicated work without making the condition harder to read.
Without :=, the last example would need to compute the score before the if and then use it again inside the block. That is fine too. Use the walrus operator only when it improves readability.
Python has built-in functions that replace manual indexing and repetitive loops. These idioms make data-processing code shorter without hiding the work.
Instead of maintaining a separate counter variable, use enumerate:
zip pairs up elements from two or more iterables.
These short-circuit through an iterable and return a single boolean. Think of any as "does at least one item satisfy this?" and all as "do all items satisfy this?"
The key parameter lets you sort by derived values without changing the data itself.
Using .get() with a default value is the standard pattern when a missing key has a legitimate fallback.
When you work with text data, you will lean on a small set of string methods. They are useful for preprocessing inputs, parsing outputs, and preparing prompts.
split breaks a string into a list. join does the reverse. They are not a replacement for a model tokenizer, but they are useful for basic text processing.
Messy text is the norm in real data. These methods handle the most common cleaning tasks.
These are cleaner than slicing for checking prefixes and suffixes, and they accept tuples for checking multiple patterns at once.
Python's ternary expression is value_if_true if condition else value_if_false. It is useful for simple value selection.
Keep ternary expressions short. If the logic is more complex, use a regular if/else block. Nested ternaries are legal, but they are usually harder to read than they are worth.
Python's None represents an explicit missing value. Python also has a broader concept of "truthiness", which you need to understand to avoid subtle bugs.
These values all evaluate to False in a boolean context:
Most other values are truthy. This is convenient for empty-collection checks, but it can misfire when 0, "", or [] are valid inputs.
is None and TruthinessThis is a common Python mistake. Do not use a mutable default argument:
The problem with the first version is that the default [] is created once when the function is defined, not each time it is called. Every call without an explicit collection argument shares and mutates the same list. This comes up often in AI code when you are accumulating documents, messages, examples, or results.
Key takeaways:
.get() when a missing key has a real fallback.enumerate and zip, it eliminates most index-based loops.:.2f for floats and :.1% for percentages.[start:stop:step] works on lists, strings, and (later) arrays and tensors. The same syntax transfers to NumPy and PyTorch.enumerate, zip, any/all, sorted(key=...), and dict.get() reduce boilerplate in everyday data-processing code.is None, not truthiness, when 0, "", or [] are valid values. And never use mutable default arguments.10 quizzes