From cf0733eb83cad842b91036ec03d64ff4dcbe6d75 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 15 Aug 2025 09:34:18 +0000
Subject: [PATCH 1/2] Initial plan
From 8e0e6f29f138243f03b6dd54d58b1543de53e1c2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 15 Aug 2025 09:47:42 +0000
Subject: [PATCH 2/2] Add comprehensive Python Static Typing lecture
Co-authored-by: mmcky <8263752+mmcky@users.noreply.github.com>
---
lectures/_toc.yml | 1 +
lectures/python_static_typing.md | 624 +++++++++++++++++++++++++++++++
2 files changed, 625 insertions(+)
create mode 100644 lectures/python_static_typing.md
diff --git a/lectures/_toc.yml b/lectures/_toc.yml
index 302a0a0b..e82f1a33 100644
--- a/lectures/_toc.yml
+++ b/lectures/_toc.yml
@@ -33,6 +33,7 @@ parts:
numbered: true
chapters:
- file: writing_good_code
+ - file: python_static_typing
- file: python_advanced_features
- file: debugging
- caption: Other
diff --git a/lectures/python_static_typing.md b/lectures/python_static_typing.md
new file mode 100644
index 00000000..b87775e9
--- /dev/null
+++ b/lectures/python_static_typing.md
@@ -0,0 +1,624 @@
+---
+jupytext:
+ text_representation:
+ extension: .md
+ format_name: myst
+kernelspec:
+ display_name: Python 3
+ language: python
+ name: python3
+---
+
+(python_static_typing)=
+```{raw} jupyter
+
+```
+
+# Python Static Typing
+
+```{index} single: Python; Type Hints
+```
+
+```{index} single: Python; Static Typing
+```
+
+## Overview
+
+Python is a dynamically typed language, which means that variable types are determined at runtime rather than compile time.
+
+While this flexibility is one of Python's great strengths, it can sometimes lead to runtime errors that could be caught earlier with explicit type information.
+
+Python 3.5 introduced **type hints** (also called type annotations), which allow developers to specify expected types for variables, function parameters, and return values.
+
+Type hints have several important applications:
+
+1. **Improving JIT compiler efficiency** - while Numba doesn't currently use them, future JIT compilers may leverage type information for better optimization
+2. **Better software design** - explicit types serve as documentation and help define clear interfaces
+3. **LLM code generation** - modern AI tools often generate code with type hints, making familiarity important
+4. **Static analysis and error detection** - tools like `mypy` and `pyright` can catch type-related errors before runtime
+
+This lecture will introduce you to Python's type annotation syntax and show how to use type hints effectively in your code.
+
+```{note}
+Type hints in Python are optional and do not affect runtime behavior. They are primarily used for documentation, static analysis, and tooling support.
+```
+
+## Basic Type Annotations
+
+### Variable Annotations
+
+The simplest form of type annotation is for variables:
+
+```{code-cell} python3
+# Basic type annotations
+name: str = "Alice"
+age: int = 30
+salary: float = 75000.50
+is_employed: bool = True
+
+print(f"{name} is {age} years old")
+```
+
+You can also annotate variables without immediate assignment:
+
+```{code-cell} python3
+# Forward declaration with type annotation
+count: int
+data: list
+
+count = 10
+data = [1, 2, 3, 4, 5]
+print(f"Count: {count}, Data: {data}")
+```
+
+### Function Annotations
+
+Type hints are most commonly used with functions to specify parameter types and return types:
+
+```{code-cell} python3
+def greet(name: str, age: int) -> str:
+ """Return a greeting message."""
+ return f"Hello {name}, you are {age} years old!"
+
+def calculate_tax(income: float, rate: float) -> float:
+ """Calculate tax based on income and rate."""
+ return income * rate
+
+# Using the functions
+message = greet("Bob", 25)
+tax = calculate_tax(50000.0, 0.2)
+
+print(message)
+print(f"Tax: ${tax:.2f}")
+```
+
+### Collection Types
+
+For collections, you need to import types from the `typing` module (Python 3.9+ also supports built-in generics):
+
+```{code-cell} python3
+from typing import List, Dict, Tuple, Set, Optional
+
+def process_scores(scores: List[float]) -> Dict[str, float]:
+ """Process a list of scores and return statistics."""
+ return {
+ 'mean': sum(scores) / len(scores),
+ 'max': max(scores),
+ 'min': min(scores)
+ }
+
+def get_coordinates() -> Tuple[float, float]:
+ """Return x, y coordinates."""
+ return (3.14, 2.71)
+
+def find_unique_words(text: str) -> Set[str]:
+ """Return unique words from text."""
+ return set(text.lower().split())
+
+# Example usage
+test_scores = [85.5, 92.0, 78.3, 95.7, 88.1]
+stats = process_scores(test_scores)
+coords = get_coordinates()
+words = find_unique_words("The quick brown fox jumps over the lazy dog")
+
+print(f"Score statistics: {stats}")
+print(f"Coordinates: {coords}")
+print(f"Unique words: {len(words)} words")
+```
+
+### Optional and Union Types
+
+Use `Optional` for values that might be `None`, and `Union` for values that could be one of several types:
+
+```{code-cell} python3
+from typing import Optional, Union
+
+def divide(a: float, b: float) -> Optional[float]:
+ """Divide two numbers, return None if division by zero."""
+ if b == 0:
+ return None
+ return a / b
+
+def process_id(user_id: Union[int, str]) -> str:
+ """Process user ID, which can be either int or string."""
+ return f"User ID: {user_id}"
+
+# Example usage
+result1 = divide(10, 2)
+result2 = divide(10, 0)
+print(f"10/2 = {result1}")
+print(f"10/0 = {result2}")
+
+print(process_id(123))
+print(process_id("abc123"))
+```
+
+## Advanced Type Features
+
+### Generic Types
+
+Generics allow you to write functions and classes that work with multiple types:
+
+```{code-cell} python3
+from typing import TypeVar, Generic, List
+
+T = TypeVar('T')
+
+def get_first_item(items: List[T]) -> Optional[T]:
+ """Get the first item from a list."""
+ if items:
+ return items[0]
+ return None
+
+def reverse_list(items: List[T]) -> List[T]:
+ """Reverse a list maintaining the same type."""
+ return items[::-1]
+
+# Example usage with different types
+numbers = [1, 2, 3, 4, 5]
+words = ["apple", "banana", "cherry"]
+
+first_number = get_first_item(numbers) # Type is Optional[int]
+first_word = get_first_item(words) # Type is Optional[str]
+
+reversed_numbers = reverse_list(numbers) # Type is List[int]
+reversed_words = reverse_list(words) # Type is List[str]
+
+print(f"First number: {first_number}")
+print(f"First word: {first_word}")
+print(f"Reversed numbers: {reversed_numbers}")
+print(f"Reversed words: {reversed_words}")
+```
+
+### Custom Classes with Type Hints
+
+Type hints work well with custom classes:
+
+```{code-cell} python3
+from typing import List, Optional
+from dataclasses import dataclass
+
+@dataclass
+class Point:
+ """A point in 2D space."""
+ x: float
+ y: float
+
+ def distance_from_origin(self) -> float:
+ """Calculate distance from origin."""
+ return (self.x**2 + self.y**2)**0.5
+
+class Portfolio:
+ """A simple investment portfolio."""
+
+ def __init__(self, name: str) -> None:
+ self.name = name
+ self.holdings: Dict[str, float] = {}
+
+ def add_stock(self, symbol: str, shares: float) -> None:
+ """Add shares of a stock to the portfolio."""
+ if symbol in self.holdings:
+ self.holdings[symbol] += shares
+ else:
+ self.holdings[symbol] = shares
+
+ def get_holding(self, symbol: str) -> Optional[float]:
+ """Get number of shares for a given stock."""
+ return self.holdings.get(symbol)
+
+ def total_positions(self) -> int:
+ """Return total number of different stock positions."""
+ return len(self.holdings)
+
+# Example usage
+point = Point(3.0, 4.0)
+print(f"Distance from origin: {point.distance_from_origin():.2f}")
+
+portfolio = Portfolio("My Portfolio")
+portfolio.add_stock("AAPL", 100)
+portfolio.add_stock("GOOGL", 50)
+print(f"AAPL holding: {portfolio.get_holding('AAPL')}")
+print(f"Total positions: {portfolio.total_positions()}")
+```
+
+## Type Checking with mypy
+
+While Python doesn't enforce type hints at runtime, you can use static type checkers like `mypy` to validate your code:
+
+```{code-cell} python3
+# This function has a type error that mypy would catch
+def add_numbers(a: int, b: int) -> int:
+ """Add two integers."""
+ return a + b
+
+# This would cause a type error in mypy:
+# result = add_numbers("hello", 5) # Error: str is not compatible with int
+
+# Correct usage:
+result = add_numbers(10, 20)
+print(f"Result: {result}")
+```
+
+To check your code with mypy, you would run:
+```bash
+pip install mypy
+mypy your_file.py
+```
+
+## Applications in Scientific Computing
+
+### NumPy Arrays
+
+Type hints work well with NumPy arrays:
+
+```{code-cell} python3
+import numpy as np
+from typing import Union
+
+# Type alias for NumPy arrays
+FloatArray = np.ndarray
+
+def normalize_vector(vec: FloatArray) -> FloatArray:
+ """Normalize a vector to unit length."""
+ norm = np.linalg.norm(vec)
+ if norm == 0:
+ return vec
+ return vec / norm
+
+def compute_statistics(data: FloatArray) -> Dict[str, float]:
+ """Compute basic statistics for an array."""
+ return {
+ 'mean': float(np.mean(data)),
+ 'std': float(np.std(data)),
+ 'min': float(np.min(data)),
+ 'max': float(np.max(data))
+ }
+
+# Example usage
+vector = np.array([3.0, 4.0])
+normalized = normalize_vector(vector)
+print(f"Original: {vector}, Normalized: {normalized}")
+
+data = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
+stats = compute_statistics(data)
+print(f"Statistics: {stats}")
+```
+
+### Economic Modeling Example
+
+Here's a more comprehensive example showing type hints in an economic context:
+
+```{code-cell} python3
+from typing import NamedTuple, List, Callable
+import numpy as np
+
+class EconomicParameters(NamedTuple):
+ """Parameters for a simple economic model."""
+ alpha: float # Capital share
+ beta: float # Discount factor
+ delta: float # Depreciation rate
+
+class ModelResult(NamedTuple):
+ """Results from economic model simulation."""
+ capital: np.ndarray
+ consumption: np.ndarray
+ utility: float
+
+def utility_function(consumption: float, gamma: float = 2.0) -> float:
+ """CRRA utility function."""
+ if gamma == 1.0:
+ return np.log(consumption)
+ else:
+ return (consumption**(1 - gamma) - 1) / (1 - gamma)
+
+def simulate_growth_model(
+ params: EconomicParameters,
+ k0: float,
+ periods: int = 100
+) -> ModelResult:
+ """Simulate a simple growth model."""
+ capital = np.zeros(periods + 1)
+ consumption = np.zeros(periods)
+ capital[0] = k0
+
+ for t in range(periods):
+ # Simple optimal policy (not solving the full problem)
+ investment_rate = 0.3
+ output = capital[t] ** params.alpha
+ investment = investment_rate * output
+ consumption[t] = output - investment
+ capital[t + 1] = (1 - params.delta) * capital[t] + investment
+
+ # Calculate total discounted utility
+ total_utility = sum(
+ (params.beta ** t) * utility_function(consumption[t])
+ for t in range(periods)
+ )
+
+ return ModelResult(capital[:-1], consumption, total_utility)
+
+# Example usage
+params = EconomicParameters(alpha=0.33, beta=0.95, delta=0.1)
+result = simulate_growth_model(params, k0=1.0, periods=50)
+
+print(f"Final capital: {result.capital[-1]:.3f}")
+print(f"Average consumption: {np.mean(result.consumption):.3f}")
+print(f"Total utility: {result.utility:.3f}")
+```
+
+## Limitations and Considerations
+
+### Numba Compatibility
+
+Currently, Numba does not use Python type hints for compilation. You still need to use Numba's decorator syntax:
+
+```{code-cell} python3
+import numba as nb
+
+# Type hints are ignored by Numba
+@nb.jit
+def fast_sum(arr: np.ndarray) -> float:
+ """Fast sum using Numba - type hints ignored."""
+ total = 0.0
+ for i in range(len(arr)):
+ total += arr[i]
+ return total
+
+# Numba-specific type specification (current approach)
+@nb.jit(nb.float64(nb.float64[:]))
+def fast_sum_numba(arr):
+ """Fast sum with Numba type specification."""
+ total = 0.0
+ for i in range(len(arr)):
+ total += arr[i]
+ return total
+
+# Test both functions
+test_array = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
+print(f"Sum with type hints: {fast_sum(test_array)}")
+print(f"Sum with Numba types: {fast_sum_numba(test_array)}")
+```
+
+```{note}
+According to the [Numba documentation](https://stackoverflow.com/questions/42310278/using-python-type-hints-with-numba), type hints are not yet supported for JIT compilation. However, this may change in future versions.
+```
+
+### JAX Type Annotations
+
+JAX has a [roadmap for supporting Python type annotations](https://docs.jax.dev/en/latest/jep/12049-type-annotations.html), which will eventually provide better integration:
+
+```{code-cell} python3
+# This is more of a conceptual example
+# JAX type annotation support is still in development
+
+import jax.numpy as jnp
+
+def jax_function(x: jnp.ndarray) -> jnp.ndarray:
+ """JAX function with type hints."""
+ return jnp.sin(x) + jnp.cos(x**2)
+
+# Example usage
+x = jnp.array([1.0, 2.0, 3.0])
+result = jax_function(x)
+print(f"JAX result: {result}")
+```
+
+## Best Practices
+
+1. **Start gradually**: Add type hints to new code and gradually retrofit existing code
+2. **Focus on interfaces**: Prioritize type hints for function signatures and public APIs
+3. **Use type aliases**: Create readable aliases for complex types
+4. **Be consistent**: Maintain consistent typing style across your codebase
+5. **Leverage tools**: Use mypy or pyright for static type checking
+6. **Document with types**: Let type hints serve as part of your documentation
+
+```{code-cell} python3
+# Example of good type hint practices
+from typing import List, Dict, TypeAlias
+
+# Type aliases for clarity
+Price = float
+Quantity = int
+StockData = Dict[str, Price]
+
+def calculate_portfolio_value(
+ holdings: Dict[str, Quantity],
+ prices: StockData
+) -> Price:
+ """Calculate total portfolio value.
+
+ Args:
+ holdings: Dictionary mapping stock symbols to quantities
+ prices: Dictionary mapping stock symbols to current prices
+
+ Returns:
+ Total portfolio value
+ """
+ total_value = 0.0
+ for symbol, quantity in holdings.items():
+ if symbol in prices:
+ total_value += quantity * prices[symbol]
+ return total_value
+
+# Example usage
+my_holdings = {"AAPL": 100, "GOOGL": 50}
+current_prices = {"AAPL": 150.0, "GOOGL": 2500.0}
+
+portfolio_value = calculate_portfolio_value(my_holdings, current_prices)
+print(f"Portfolio value: ${portfolio_value:,.2f}")
+```
+
+## Tools and Integration
+
+### Modern Type Checking Tools
+
+1. **mypy**: The original Python type checker
+2. **pyright/pylance**: Microsoft's type checker used in VS Code
+3. **pyre**: Facebook's type checker for large codebases
+
+### IDE Integration
+
+Most modern IDEs provide excellent support for type hints:
+
+- **VS Code**: Built-in support with Pylance extension
+- **PyCharm**: Native type checking and completion
+- **Vim/Neovim**: Via language server protocol (LSP)
+
+## Exercises
+
+### Exercise 1
+
+Write a function `calculate_compound_interest` with proper type hints that:
+- Takes principal amount, annual interest rate, and number of years
+- Returns the final amount after compound interest
+- Include proper type annotations for all parameters and return value
+
+```{code-cell} python3
+def calculate_compound_interest(principal: float, rate: float, years: int) -> float:
+ """Calculate compound interest.
+
+ Args:
+ principal: Initial amount invested
+ rate: Annual interest rate (as decimal, e.g., 0.05 for 5%)
+ years: Number of years
+
+ Returns:
+ Final amount after compound interest
+ """
+ return principal * (1 + rate) ** years
+
+# Test the function
+result = calculate_compound_interest(1000.0, 0.05, 10)
+print(f"$1000 at 5% for 10 years: ${result:.2f}")
+```
+
+### Exercise 2
+
+Create a class `BankAccount` with type hints that:
+- Stores account holder name and balance
+- Has methods to deposit, withdraw, and check balance
+- Uses appropriate type hints throughout
+
+```{code-cell} python3
+from typing import Optional
+
+class BankAccount:
+ """A simple bank account with type annotations."""
+
+ def __init__(self, account_holder: str, initial_balance: float = 0.0) -> None:
+ self.account_holder = account_holder
+ self.balance = initial_balance
+
+ def deposit(self, amount: float) -> None:
+ """Deposit money into the account."""
+ if amount > 0:
+ self.balance += amount
+
+ def withdraw(self, amount: float) -> bool:
+ """Withdraw money from account. Returns True if successful."""
+ if 0 < amount <= self.balance:
+ self.balance -= amount
+ return True
+ return False
+
+ def get_balance(self) -> float:
+ """Get current account balance."""
+ return self.balance
+
+ def __str__(self) -> str:
+ return f"Account({self.account_holder}): ${self.balance:.2f}"
+
+# Test the class
+account = BankAccount("Alice", 1000.0)
+account.deposit(500.0)
+success = account.withdraw(200.0)
+print(f"Withdrawal successful: {success}")
+print(account)
+```
+
+### Exercise 3
+
+Write a function that processes economic time series data with proper type annotations:
+
+```{code-cell} python3
+from typing import Tuple
+import numpy as np
+
+def analyze_time_series(
+ data: np.ndarray,
+ window_size: int = 5
+) -> Tuple[np.ndarray, np.ndarray, float]:
+ """Analyze time series data.
+
+ Args:
+ data: Time series data as numpy array
+ window_size: Size of moving average window
+
+ Returns:
+ Tuple of (moving_averages, differences, volatility)
+ """
+ # Calculate moving averages
+ moving_averages = np.convolve(data, np.ones(window_size)/window_size, mode='valid')
+
+ # Calculate first differences
+ differences = np.diff(data)
+
+ # Calculate volatility (standard deviation of differences)
+ volatility = float(np.std(differences))
+
+ return moving_averages, differences, volatility
+
+# Test with sample data
+sample_data = np.array([100, 102, 98, 105, 103, 107, 104, 109, 106, 111])
+ma, diffs, vol = analyze_time_series(sample_data, window_size=3)
+
+print(f"Moving averages: {ma}")
+print(f"First differences: {diffs}")
+print(f"Volatility: {vol:.2f}")
+```
+
+## Summary
+
+Python type hints provide a powerful way to make your code more readable, maintainable, and less error-prone. While they don't affect runtime behavior, they offer significant benefits:
+
+- **Documentation**: Type hints serve as inline documentation for your code
+- **IDE Support**: Better autocomplete, refactoring, and error detection
+- **Static Analysis**: Catch errors before runtime using tools like mypy
+- **Team Collaboration**: Clearer interfaces make code easier to understand and maintain
+
+As the Python ecosystem evolves, type hints are becoming increasingly important, especially with the rise of AI-generated code and more sophisticated development tools.
+
+Key takeaways:
+- Start with function signatures and gradually add more detailed typing
+- Use type checkers like mypy to validate your annotations
+- Consider type hints as part of your code documentation strategy
+- Be aware of current limitations with performance libraries like Numba
+- Stay informed about developments in JAX and other scientific computing libraries
+
+```{note}
+For more information on Python typing, see the [official documentation](https://docs.python.org/3/library/typing.html) and the [mypy documentation](https://mypy.readthedocs.io/).
+```
\ No newline at end of file