1.4.1Python & Scientific Computing

Python syntax, data types, control flow

3,765 words17 min readdifficulty · medium1 backlinks

Why Python for AI/ML?

Python dominates AI/ML for three reasons:

  1. Readable syntax → faster protyping and debugging
  2. Rich ecosystem → NumPy, Pandas, Scikit-learn, PyTorch, TensorFlow
  3. Dynamic typing → rapid experimentation (trade computational speed for development speed)

The dynamic typing means Python checks types at runtime, not compile-time. This flexibility is why you can quickly test ML hypotheses, but also why you need to understand types deeply to avoid subtle bugs.


Python Data Types: The Building Blocks

Why These Types Matter in ML

Integers and Floats: Represent features, labels, hyperparameters. Float precision affects numerical stability in gradient descent.

Lists vs Tuples: Lists for mutable data (e.g., collecting predictions), tuples for immutable configs (e.g., input_shape=(28, 28, 1)).

Dictionaries: Store model hyperparameters, config files, feature mappings. O(1) average lookup time.

None: Represent missing data before imputation, uninitialized model weights.

Type Conversion andercion

Python has explicit type conversion (no automatic coercion between numeric types in operations that could lose precision):

# Explicit conversions
x = int(3.7)      # 3 (truncates, doesn't round)
y = float("2.5")  # 2.5
z = str(42)       # "42"
 
# Mixed arithmetic promotes to most general type
result = 5 + 2.0  # 7.0 (int + float → float)

Why explicit? Prevents silent precision loss bugs. In ML, accidentally converting float weights to int would destroy model performance.


Python Syntax: Indentation as Grammar

# Correct indentation
if accuracy > 0.95:
    print("Model converged")
    save_checkpoint()  # Part of if-block
print("Training complete")  # Outside if-block
 
# WRONG: Inconsistent indentation causes IndentationError
if accuracy > 0.95:
    print("Model converged")
      save_checkpoint()  # ERROR: unexpected indent

Why this design? Forces readable code. In ML research code shared across teams, consistent structure reduces cognitive load.

Variables and Assignment

Python uses reference semantics:

# Assignment creates a reference, not a copy
weights_original = [0.5, 0.3, 0.8]
weights_backup = weights_original  # Both point to SAME list
 
weights_backup[0] = 0.9  # Modifies the original too!
print(weights_original)  # [0.9, 0.3, 0.8]
 
# Correct: explicit copy
import copy
weights_backup = copy.deepcopy(weights_original)

Why this matters in ML: Neural network layers often share weight references. Modifying one reference affects all unless you explicitly copy.

learning_rate = 0.01  # Global
 
def train_model():
    learning_rate = 0.001  # Local (shadows global)
    
    def update_weights():
        # Uses enclosing function's learning_rate (0.001)
        print(f"LR: {learning_rate}")
    
    update_weights()
 
train_model()  # Prints "LR: 0.001"

Control Flow: Directing Computation

Conditional Statements

Truthiness in Python: Objects have inherent boolean values. Empty containers ([], {}, "), 0, `None are falsy. All others are truthy.

# Checking if list is non-empty (Pythonic)
predictions = [0.9, 0.3, 0.7]
 
if predictions:  # Truthy because list is non-empty
    avg = sum(predictions) / len(predictions)

Loops: For and While

List Comprehensions: Concise syntax for creating lists from iterables.

# Standard loop
squared = []
for x in range(10):
    squared.append(x ** 2)
 
# List comprehension (Pythonic)
squared = [x ** 2 for x in range(10)]
 
# With condition (filter)
even_squared = [x ** 2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]
 
# Why use this? More readable, often faster (optimized C implementation).
# Common in data preprocessing: [preprocess(x) for x in dataset]

Loop Control: Break and Continue

  • break: Immediately exit innermost loop
  • continue: Skip remaining loop body, proceed to next iteration
# Skip corrupted data samples
for i, sample in enumerate(dataset):
    if is_corrupted(sample):
        print(f"Skipping sample {i}")
        continue  # Skip to next iteration
    if i >= max_samples:
        break  # Exit loop entirely
    
    process(sample)

Functions: Encapsulation and Reusability

Argument Passing

Python uses pass-by-object-reference:

  • Immutable objects (int, float, str, tuple): Behave like pass-by-value (can't modify)
  • Mutable objects (list, dict, set): Modifications inside function affect original
def modify_list(lst):
    lst.append(4)  # Modifies original list
 
def modify_int(x):
    x += 10  # Creates new local int, doesn't affect original
 
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list)  # [1, 2, 3, 4]—modified!
 
my_int = 5
modify_int(my_int)
print(my_int)  # 5—unchanged

Why this matters in ML: Passing large arrays/tensors to functions doesn't copy data (efficient), but unexpected modifications can cause bugs.

*args and **kwargs

def train_model(model, data, *args, **kwargs):
    """
    *args: Positional arguments as tuple
    **kwargs: Keyword arguments as dict
    """
    epochs = kwargs.get('epochs', 100)  # Default 100
    lr = kwargs.get('learning_rate', 0.01)
    
    print(f"Extra args: {args}")
    print(f"Extra kwargs: {kwargs}")
 
# Call with various arguments
train_model(my_model, my_data, "extra", "values", 
            epochs=50, learning_rate=0.001, batch_size=32)
# args = ("extra", "values")
# kwargs = {'epochs': 50, 'learning_rate': 0.001, 'batch_size': 32}

Use case: Creating flexible APIs (like Keras model.fit() which accepts many optional hyperparameters).


Exception Handling

EAFP vs LBYL: Python favors "Easier to Ask Forgiveness than Permission" (try-except) over "Look Before You Leap" (if checks). Exceptions are not expensive in Python when uncomon.


Comprehensions Beyond Lists

Dictionary Comprehension:

# Create feature name to index mapping
feature_names = ['age', 'income', 'score']
feature_to_idx = {name: i for i, name in enumerate(feature_names)}
# {'age': 0, 'income': 1, 'score': 2}

Set Comprehension:

# Unique labels in dataset
labels = [0, 1, 0, 2, 1, 2]
unique_labels = {label for label in labels}
# {0, 1, 2}

Generator Expression (memory-efficient, lazy evaluation):

# Process large dataset without loading all into memory
data_generator = (preprocess(x) for x in huge_dataset)
 
# Values computed on-demand during iteration
for item in data_generator:
    train_on(item)

Why generators in ML? When dataset doesn't fit in RAM. TensorFlow's tf.data and PyTorch's DataLoader use this principle.


Recall Explain to a 12-year-old

Imagine you're teaching a robot to cook. Python is the instruction booklet language.

Data types are like ingredient categories: numbers (how many eggs), text (recipe name), lists (shopping list you can change), tuples (ingredient ratios that never change).

Control flow is the recipe steps: "IF oven is hot THEN put cake in, ELSE wait." Or "FOR each of 12 cupcakes, add frosting."

Functions are sub-recipes: Instead of writing "mix flour, sugar, butter" every time, you create a "make_dough()" recipe you can reuse.

Indentation (those spaces before lines) shows which instructions go together—like how recipe steps under "Baking" are indented under that heading.

In ML, you're teaching computers to learn patterns. Python is how you give those teaching instructions clearly. Just like recipes need exact measurements, ML code needs exact data types. Just like you might say "keep mixing UNTIL smooth," ML uses WHILE loops to train UNTIL the model is good enough.



Connections NumPy arrays and vectorization - Built on Python lists but optimized for numerical computation


Practice Problems

  1. Write a function that takes a list of model predictions and returns accuracy, precision, and recall.
  2. Implement early stopping with a while loop that tracks validation loss.
  3. Create a dict comprehension that maps layer names to parameter counts in a neural network.
  4. Debug this code: Why does modifying layer_config inside a function change the original?

Figure — Python syntax, data types, control flow

#flashcards/ai-ml

What is Python's LEGB rule for variable resolution? :: Local → Enclosing → Global → Built-in. Python searches these scopes in order to resolve variable names.

Why does Python use indentation for code blocks instead of braces?
Enforces readable code structure. Reduces cognitive load and ensures consistent style across codebases—critical for collaborative ML research.
What's the difference between = and == in Python?
= is assignment (creates/updates variable reference). == is comparison (tests equality, returns boolean).
What happens when you do y = x where x is a list?
Both y and x reference the SAME list object. Modifying y modifies x too. Use copy.deepcopy() for independent copy.
When should you use a while loop vs a for loop in ML?
For loop: Known iterations (epochs, batches). While loop: Unknown iterations (train until convergence, early stopping based on metrics).

What does range(0, 100, 32) produce? :: [0, 32, 64, 96]. Start at 0, step by 32, stop before 100. Useful for batch indices.

Why is int vs float important in ML? :: Float precision affects numerical stability in gradient descent. Integer for discrete counts (epochs, batch indices). Mixing incorrectly causes shape errors or precision loss.

What's the difference between break and continue?
break exits the entire loop immediately. continue skips remaining statements in current iteration and jumps to next iteration.
What does if predictions: check?
Whether predictions is truthy. Empty list/dict/string, 0, None are falsy. Non-empty containers and non-zero numbers are truthy.
Why use list comprehensions over for loops?
More concise, readable, and often faster (optimized C implementation). Common pattern: [preprocess(x) for x in dataset].
What's the output of [x**2 for x in range(5) if x % 2 == 0]?
[0, 4, 16]. Squares only even numbers from 0-4.
What's the difference between *args and **kwargs?
*args captures positional arguments as tuple. **kwargs captures keyword arguments as dict. Used for flexible function APIs.
Why use try-except instead of if-checks in Python?
EAFP principle: "Easier to Ask Forgiveness than Permission." Try-except is idiomatic Python, not expensive when exceptions are rare.
What does finally do in exception handling?
Always executes, even if exception occurs or return statement in try block. Used for cleanup (closing files, releasing resources).
Why are generators memory-efficient?
Lazy evaluation—values computed on-demand during iteration, not stored in memory. Critical when dataset doesn't fit in RAM.
What's the difference between list and tuple?
List is mutable (can modify elements), tuple is immutable (fixed after creation). Use tuples for config that shouldn't change.
How do you convert float3.7 to int in Python?
int(3.7) returns 3 (truncates toward zero, doesn't round). Use round(3.7) then int() for rounding.
What's the result of 5 + 2.0 in Python?
7.0 (float). Mixed arithmetic promotes to most general type (int + float → float).
Why does dataset_size / batch_size return float in Python3?
/ always performs true division (returns float). Use // for floor division (returns int), needed for discrete step counts.
What happens if you modify a list passed to a function?
Modifications affect the original list (mutable object, pass-by-reference). Immutable types (int, str) create new local objects.

Concept Map

advantage

advantage

advantage

means

requires

enables

built on

includes

includes

includes

precision affects

prevents

Python for AI-ML

Readable Syntax

Rich Ecosystem

Dynamic Typing

Runtime Type Checks

Data Types

Numeric int float complex

Sequences str list tuple

dict hash table

Explicit Conversion

Control Flow

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, Python ko AI/ML ki "duniya ki common language" isliye kehte hain kyun ki ye ek unique balance deta hai — code padhne mein aasan (human readable) hota hai, aur phir bhi bahut powerful hai. Jaise kisi bhi bhasha ko bolne ke liye pehle uski grammar seekhni padti hai, waise hi ML ke bade concepts express karne ke liye tumhe Python ka syntax, data types, aur control flow samajhna zaroori hai. Har TensorFlow model, har NumPy array ka kaam, har data pipeline — sab yahin se, in fundamentals se, shuru hota hai. Isliye ye foundation strong hona bahut matter karta hai.

Ab data types ki baat karein — ye tumhare building blocks hain. int, float, list, tuple, dict, None — ye sab ML mein alag-alag kaam aate hain. Jaise floats tumhare features aur weights represent karte hain, aur unki precision gradient descent ki numerical stability pe asar daalti hai. Ek important cheez: Python "dynamic typing" use karta hai, matlab type runtime pe check hoti hai, compile-time pe nahi. Ye flexibility experiments jaldi karne mein help karti hai, lekin isi wajah se tumhe types deeply samajhna padta hai warna chhote-chhote subtle bugs aa jaate hain. Ek classic example — training steps nikalte waqt / (normal division) float deta hai jaise 31.25, par tumhe // (floor division) chahiye kyunki tum 0.25 batch update nahi kar sakte, steps hamesha integer hone chahiye.

Aur ek cheez yaad rakho — Python "strong typing" bhi rakhta hai, yani woh apne aap incompatible types convert nahi karega. Jaise "Epoch " + 5 seedha error dega; tumhe khud str(5) likhna padega. Ye design jaan-boojh kar hai taaki silent precision-loss wale bugs na ho — socho agar tumhare float weights galti se int mein convert ho jaayein, toh poora model performance barbaad ho jaayega. Iske alawa Python indentation (spaces) se code blocks banata hai, braces se nahi — aur ye syntactically enforced hai, sirf style nahi. Ye sab basics chhoti lagti hain, par inhe theek se samajhna hi tumhe aage confidently ML code likhne aur debug karne layak banata hai.

Go deeper — visual, from zero

Test yourself — Python & Scientific Computing

Connections