AI-assisted Python Coding

Day 1: Python Foundations and AI-assisted coding

Klaus Reygers

Studierendentage Physik, Uni Heidelberg, 23.-27.03.2026

Outline

  • Day 1: Python foundations and AI-assisted coding
  • Day 2: Data structures, functions, I/O, and debugging
  • Day 3: NumPy and Matplotlib
  • Day 4: SciPy and SymPy
  • Day 5: Pandas, scikit-learn, and good coding practice

Why learn coding in the age of coding agents?

  • Understand what the agent does — you need to read and evaluate code to catch errors, spot bugs, and avoid security issues in AI-generated output
  • Write better prompts — knowing how to code lets you describe the task precisely; vague input → broken code
  • Critical thinking stays with you — choosing algorithms, interpreting results, spotting numerical pitfalls requires domain expertise, not just typing

Programming is understanding. — Kristen Nygaard

Why Python

Advantages:

  • Rich ecosystem of well-tested libraries (NumPy, SciPy, Matplotlib, etc.)
  • Easy to learn
  • Readable and easy to understand code
  • Excellent documentation and large community

Disadvantages:

  • Slower than compiled languages (C++, Fortran)
  • Not ideal for highly computationally intensive tasks (alternative: Julia)

Books for this course

My programming background

  • BASIC on a Commodore C64
  • Assembler on a MOS 6510 CPU (C64)
  • Pascal (in school, in particular UCSD Pascal and Turbo Pascal)
  • FORTRAN 77 (for doctoral thesis)
  • C/C++
  • Mathematica/Wolfram language
  • Perl
  • Python
  • Julia
  • AI-assisted coding with OpenAI macOS codex App

Setting up your Python environment

Recommended minimal setup (terminal):

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -U pip
python -m pip install numpy scipy matplotlib pandas jupyter

Save package versions for reproducibility:

python -m pip freeze > requirements.txt

Jupyter notebooks for interactive work

Why notebooks are useful:

  • run code cell by cell
  • combine code, plots, and notes in one document
  • ideal for exploration and teaching

Start Jupyter locally:

jupyter lab

Practical tip:

  • use notebooks for exploration
  • move stable code later into .py files

LLMs in coding

  • Programming languages have evolved from low-level (e.g., assembly) to high-level languages like Python
  • AI-assisted coding is the next level of abstraction
  • Programming is now becoming a dialogue between humans and AI (transformative moment!)
  • Besides acting as a coding assistant, LLMs help
    • help clarify concepts
    • brainstorm ideas
    • improve user understanding

Types of AI assistance

  • Code understanding
  • Code generation
  • Code debugging
  • Code optimization
  • Code translation
  • Code learning

Reference: arXiv:2410.02156

AI Coding Assistants: LLMs vs assistants vs agents

Chat LLMs (ask/answer)

  • ChatGPT, Claude, Gemini
  • Best for: explanations, debugging help, learning step-by-step

IDE assistants (inline suggestions)

  • GitHub Copilot, Cursor, Amazon Q Developer, Codeium
  • Best for: autocomplete, small refactors, boilerplate

Agents (task-based, multi-step)

  • Examples: Cursor Agent, GitHub Copilot (Agent/Edits), Claude Code — plan + edit multiple files + run tools/tests
  • Best for: bigger changes, setup, iterative “run → fix” cycles

GitHub Copilot: code completion

GitHub Copilot: chat

Codex app: example

Local LLMs (run on your laptop)

Why local? privacy/offline, no data upload (but slower, smaller models).

  • LM Studio: download a model → chat locally or start a local server (often OpenAI-compatible)
  • Ollama: ollama pull <model> / ollama run <model> (easy CLI + integrates with editors)

Good open models for coding (prefer Instruct/Coder variants)

  • Qwen2.5-Coder (good all-round)
  • DeepSeek-Coder (strong code reasoning)

Practical tips

  • Start with 7B–14B models; use quantized versions for laptops.
  • For coding tasks, include: goal, files/snippets, error message, and expected output.

YoKI (Uni Heidelberg): Open-source LLMs on-prem

  • University-hosted platform with multiple open-source LLMs (no public cloud)
  • Access: only from the university network or via VPN (login with Uni-ID / project number)
  • Currently listed models include GPT OSS and Qwen 3
  • Different assistants are available with different strengths (e.g., writing vs. coding support)
  • Good for: protected experimentation in teaching/research, text + coding assistance

Links: URZ: YoKI · Weboberfläche

Takeaway:
AI can help you learn and write code,
but you still need to understand and check the results.

Python as a calculator (operators)

2 + 3
5
7 / 2, 7 // 2, 7 % 2   # division, integer division, modulo
(3.5, 3, 1)

Common operators: + - * / // % **.

Assignment operators: +=, *=, …

Useful for “update a variable”.

dt = 0.1
t = 0.0

t += dt   # same as: t = t + dt
t *= 2    # same as: t = t * 2
t
0.2

Also common: -=, /=, **=, %=.

Variables + numbers (physics example)

g = 9.81   # m/s^2
t = 2.0    # s
s = 0.5 * g * t**2
s
19.62

Tip: use meaningful names + units in comments.

Average speed of an O2 molecule

Estimate the average speed of an O\(_2\) molecule at room temperature using

\[ \bar{v} = \sqrt{\frac{8 k_B T}{\pi m}} \]

Use:

  • k_B = 1.380649e-23 J/K
  • T = 300 K
  • m = 32 * 1.66054e-27 kg for one O\(_2\) molecule
  • pi = 3.14159

Compute the average speed in m/s and km/h

Use only Python operators and **0.5 for the square root.

Solution: Average speed of an O2 molecule

k_B = 1.380649e-23
T = 300
m = 32 * 1.66054e-27
pi = 3.14159
v_avg = (8 * k_B * T / (pi * m)) ** 0.5
v_avg, v_avg * 3.6
(445.52579036366706, 1603.8928453092014)

What does 5^3 mean in Python?

Before running the code, predict the result of:

5^3

Questions:

What value does Python return? Why is it not 125?

Hint: Ask your LLM or coding assistant: “What does ^ mean in Python? Explain bitwise XOR with the binary representation of 5 and 3.”

Solution:

5^3, 5**3
(6, 125)

^ is bitwise XOR — it operates bit by bit:

  0101   (5)
^ 0011   (3)
------
  0110   (6)

Basic types (dynamic typing)

Python is dynamically typed:

  • variable types are checked at runtime (the value has a type)
  • variables can be reassigned.
x = 3
type(x)
int
x = "three"
type(x)
str

Type conversions (int, float, str)

Sometimes you need to convert types explicitly:

n = 5
float(n), str(n)
(5.0, '5')
int(3.9), float(3)
(3, 3.0)

Note: int(3.9) truncates to 3 (it does not round).

Strings: text + f-strings

Strings are used for labels, messages, file names, units.

s = 19.62
unit = "m"
f"Distance: {s:.2f} {unit}"
'Distance: 19.62 m'
name = "Klaus"
f"Hello {name}!"  # f-string inserts variables
'Hello Klaus!'

f-strings: formatting numbers and text

Format is f"...{value:FORMAT}...".

x = 12.34567
n = 7
label = "E"

(
  f"{label} = {x:.2f}",        # 2 digits after decimal
  f"{x:.3e}",                  # scientific notation
  f"n = {n:04d}",              # pad integer with zeros
  f"x = {x:8.2f}",             # total field width 8 (including everything), 2 decimals
  f"[{label:>6}] [{label:<6}]" # align right / left
)
('E = 12.35', '1.235e+01', 'n = 0007', 'x =    12.35', '[     E] [E     ]')

Tip: use :.3g for “3 significant digits” when values span many orders of magnitude.

Strings: escape characters and backslashes

Some characters have special meaning in strings:

  • \n new line
  • \t tab
  • \\ a literal backslash
print("Line 1\nLine 2")
print("x\ty")
print("A backslash: \\")
Line 1
Line 2
x   y
A backslash: \

If you want to print LaTeX-like commands, use a raw string:

latex = r"\sin^2 x + \cos^2 x"
latex
'\\sin^2 x + \\cos^2 x'

Split a string into a list (split)

Useful for parsing simple data (e.g. from a file or copy-paste).

line = "0.0, 0.10, 0.21, 0.31"  # times in seconds
parts = line.split(",")
parts
['0.0', ' 0.10', ' 0.21', ' 0.31']

Often you also want to strip spaces and convert to numbers:

times = [float(p.strip()) for p in parts]
times
[0.0, 0.1, 0.21, 0.31]

Number formatting

Print the number 254 in:

  1. Binary (base 2)
  2. Hexadecimal (uppercase)

using f-string formatting.

Solution:

n = 254
f"binary: {n:b}, hex: {n:X}"
'binary: 11111110, hex: FE'

Numerical formatting with f-strings

You measured the decay rate of a sample:

sample = "Co-60"
rate = 12345.6789
uncertainty = 12.34
time_s = 3600

Create one f-string that prints exactly this information in a compact report:

  1. the sample name
  2. the rate in scientific notation with 3 significant digits after the decimal point
  3. the uncertainty with 2 decimals
  4. the measuring time as a zero-padded 5-digit integer

Target style, for example:

sample=Co-60, rate=1.235e+04 1/s, sigma=12.34 1/s, time=03600 s

Solution: Numerical formatting with f-strings

sample = "Co-60"
rate = 12345.6789
uncertainty = 12.34
time_s = 3600
f"sample={sample}, rate={rate:.3e} 1/s, sigma={uncertainty:.2f} 1/s, time={time_s:05d} s"
'sample=Co-60, rate=1.235e+04 1/s, sigma=12.34 1/s, time=03600 s'

What is a “method”? (e.g. .append)

  • A function belongs to a module (e.g. math.sqrt(2)).
  • A method belongs to an object (e.g. a list has .append(...)).
values = []
values.append(3.14)
values.append(2.71)
values
[3.14, 2.71]

Explore string methods

Ask your LLM or coding assistant:

“What are the most common Python string methods?”

Then try out a few in a notebook, for example:

  • .upper(), .lower(), .strip()
  • .replace(), .find(), .startswith()
  • .split(), .join()

Comparisons + booleans + logic

T = 295.0  # K
is_room_temp = (290 <= T) and (T <= 300)
is_room_temp
True

Comparisons: == != < <= > >= and logic: and or not.

Common mistake: = is assignment, == is comparison.

Indentation defines blocks

In Python, indentation (spaces) defines code blocks.

v = 12.0
if v > 0:
  label = "moving"
  speed = v
else:
  label = "rest"
  speed = 0.0

label, speed
('moving', 12.0)

Rule: be consistent (usually 4 spaces). Don’t mix tabs and spaces.

for loops + range()

Use range(n) for 0, 1, ..., n-1.

g = 9.81
dt = 1.0

positions = []
for i in range(6):  # 0..5
  t = i * dt
  positions.append(0.5 * g * t**2)

positions
[0.0, 4.905, 19.62, 44.145, 78.48, 122.625]

enumerate in loops

Use enumerate(...) when you need both the index and the value.

measurements = [0.98, 1.02, 1.01, 0.99]

for i, m in enumerate(measurements):
  print(f"measurement {i}: {m}")
measurement 0: 0.98
measurement 1: 1.02
measurement 2: 1.01
measurement 3: 0.99

You can start counting at 1:

for i, m in enumerate(measurements, start=1):
  print(f"measurement {i}: {m}")
measurement 1: 0.98
measurement 2: 1.02
measurement 3: 1.01
measurement 4: 0.99

break (stop a loop early)

Example: first time when the fall distance exceeds a threshold.

g = 9.81
dt = 0.1
threshold = 5.0  # meters

for i in range(10_000):
  t = i * dt
  s = 0.5 * g * t**2
  if s >= threshold:
    break

t, s
(1.1, 5.935050000000001)

while loops (until a condition is met)

Example: fall until the ground is reached.

g = 9.81
y = 10.0   # m
v = 0.0    # m/s
dt = 0.1
t = 0.0

while y > 0:
  v = v - g * dt
  y = y + v * dt
  t = t + dt

t
1.4000000000000001

continue (skip to next loop iteration)

Example: ignore invalid measurements (e.g. sensor error codes).

measurements = [1.01, -999.0, 1.00, 1.02]  # seconds

i = 0
clean = []
while i < len(measurements):
  m = measurements[i]
  i += 1
  if m < 0:
    continue
  clean.append(m)

clean
[1.01, 1.0, 1.02]

Fibonacci threshold

Generate Fibonacci numbers using a while loop and determine:

  1. the first Fibonacci number that is greater than 1000
  2. its index in the sequence (with F0 = 0, F1 = 1)

Hint: repeatedly update two variables, for example a, b = b, a + b.

Solution:

a, b = 0, 1
index = 1  # current index of b

while b <= 1000:
  a, b = b, a + b
  index += 1

b, index
(1597, 17)