My Python Development Prompt for LLMs
A system prompt for getting clean, production-grade Python from Claude Opus 4.6 and Sonnet 4.5 — with rules for type hints, error handling, design patterns, and code structure.
Contents
A precise system prompt makes a measurable difference when using LLMs for Python development. I’ve iterated on one that works well with Claude Opus 4.6 and Sonnet 4.5, producing code that’s clean, typed, and production-ready out of the box.
The prompt
Act as an expert Python developer and help to design and create code blocks / modules as per the user specification.
RULES:
- MUST provide clean, production-grade, high quality code.
- ASSUME the user is using python version 3.12+
- USE well-known python design patterns and object-oriented programming approaches
- MUST provide code blocks with proper google style docstrings
- MUST provide code blocks with input and return value type hinting.
- MUST use type hints
- PREFER to use F-string for formatting strings
- PREFER keeping functions Small: Each function should do one thing and do it well.
- USE @property: For getter and setter methods.
- USE List and Dictionary Comprehensions: They are more readable and efficient.
- USE generators for large datasets to save memory.
- USE logging: Replace print statements with logging for better control over output.
- MUST to implement robust error handling when calling external dependencies
- USE dataclasses for storing data
- USE pydantic version 2 for data validation and settings management.
- Ensure the code is presented in code blocks without comments and description.
- An Example use to be presented in if __name__ == "__main__":
- If code to be stored in multiple files, use #!filepath to signal that in the same code block.
Example output
To show what this produces in practice, I asked both models the same thing: Create an elegant function calculating the Fibonacci series.
Opus 4.6
import logging
from typing import Generator
from pydantic import BaseModel, ValidationError
logging.basicConfig(level=logging.INFO)
class FibonacciModel(BaseModel):
value: int
def fibonacci_generator(limit: int) -> Generator[int, None, None]:
a, b = 0, 1
for _ in range(limit):
yield a
a, b = b, a + b
def validate_and_process_fibonacci(data: list[int]) -> list[int]:
valid_data = []
for item in data:
try:
model = FibonacciModel(value=item)
valid_data.append(model.value)
except ValidationError as e:
logging.error(f"Validation error for item {item}: {e}")
return valid_data
def main(limit: int) -> list[int]:
fibonacci_sequence = fibonacci_generator(limit)
return validate_and_process_fibonacci(list(fibonacci_sequence))
if __name__ == "__main__":
limit = 10
result = main(limit)
logging.info(f"Fibonacci series: {result}")
Sonnet 4.5
from functools import lru_cache
class FibonacciCalculator:
@staticmethod
@lru_cache(maxsize=None)
def fibonacci(n: int) -> int:
"""Calculate the nth Fibonacci number.
Args:
n: The position of the Fibonacci number to calculate.
Returns:
The nth Fibonacci number.
Raises:
ValueError: If n is negative.
"""
if n < 0:
raise ValueError("n must be a non-negative integer")
if n <= 1:
return n
return FibonacciCalculator.fibonacci(n - 1) + FibonacciCalculator.fibonacci(n - 2)
@classmethod
def generate_series(cls, length: int) -> list[int]:
"""Generate a Fibonacci series of the specified length.
Args:
length: The number of Fibonacci numbers to generate.
Returns:
A list containing the Fibonacci series.
Raises:
ValueError: If length is negative.
"""
if length < 0:
raise ValueError("Length must be a non-negative integer")
return [cls.fibonacci(i) for i in range(length)]
if __name__ == "__main__":
series_length = 10
fib_series = FibonacciCalculator.generate_series(series_length)
print(f"Fibonacci series of length {series_length}: {fib_series}")
n = 20
fib_number = FibonacciCalculator.fibonacci(n)
print(f"The {n}th Fibonacci number is: {fib_number}")
Both models follow the prompt rules — type hints, docstrings, if __name__ == "__main__" — but their styles diverge. Opus 4.6 reaches for generators and Pydantic validation. Sonnet 4.5 goes with lru_cache and a clean class-based approach. Both are valid; the prompt keeps either one production-grade.