I worked through Python's core data types, dynamic typing model, and safe conversion patterns using isinstance and type hints. Defensive type handling made automation scripts robust against unexpected input rather than crashing on a bad cast.

Objective & Context

Python infers types at runtime, so input validation must be explicit. This lab covers int, float, str, bool, and None, then builds safe conversion helpers that validate before casting, the foundation for reliable user and file input later.

Learning Objectives

  • Inspect runtime types with type() and isinstance() instead of guessing from values.
  • Apply type hints to function signatures and understand what they do and do not enforce.
  • Write safe conversion helpers that return a default instead of raising on bad input.
  • Distinguish mutable from immutable types and predict aliasing behaviour.
  • Choose the right numeric, text, and boolean types for automation inputs.

Environment & Prerequisites

Prerequisites

  • Python 3.11 inside an activated venv (see the Python Fundamentals lab)
  • A REPL (python) or a scratch script file for experimentation
  • Optional: mypy for static type checking (pip install mypy)
  • Assumed skill: running Python one-liners and reading tracebacks

Step-by-Step Execution

1. Inspect types at runtime

python -c "x=42; print(type(x), isinstance(x,int))"

Expected output:

<class 'int'> True

What this teaches: type() reports the exact runtime class while isinstance() answers the question you usually mean: can this value be used as an int here, including subclasses.

2. Safe conversion with a guard

cat > conv.py <<'EOF' def to_int(s: str, default: int = 0) -> int: """Convert s to int, returning default on any bad input.""" try: return int(s) except (ValueError, TypeError): return default EOF

Expected output:

(no output; conv.py written with the to_int helper)

What this teaches: The guard pattern centralizes the try/except in one helper so call sites stay readable and every conversion fails the same safe way. Saving it to conv.py now makes step 3's import work, since python -c imports modules from the current directory.

3. Verify behaviour on good and bad input

python -c "from conv import to_int; print(to_int('7'), to_int('x'))"

Expected output:

7 0

What this teaches: Good input converts normally and bad input returns the default. Testing both paths is what makes the helper trustworthy in automation. Run this from the same directory where you saved conv.py; otherwise the import fails with ModuleNotFoundError.

How It Works

How It Works

Python is dynamically typed: a variable is just a name bound to an object, and the type lives on the object, not the name. That is why x = 42 followed by x = "hello" is legal; the name is rebound to a different object with a different type.

Type hints are annotations for readers and tools, not runtime contracts. Python never checks them when the code runs, which is why isinstance() guards and conversion helpers still matter at runtime: hints document intent, guards enforce it.

The to_int pattern used here is a tiny example of defensive programming for automation. Scripts consume config files, CLI arguments, and API responses, all of which arrive as strings of uncertain quality; a helper that converts-or-defaults keeps one bad value from crashing a whole run.

Validation & Testing

Feed valid, invalid, and None inputs to the converter and confirm it never raises. Pass criteria: valid inputs cast correctly, invalid ones fall back to the default, and type hints pass mypy.

Advanced: Troubleshooting
  • Silent wrong type: Python won't warn; add isinstance checks at boundaries.
  • float precision: use decimal.Decimal for currency, not float.
  • Mutable defaults: never use a mutable default argument; use None and assign inside.

Troubleshooting

SymptomLikely causeFix
isinstance check passes but code still failsa subclass or duck-typed object satisfied the check but lacks the method usedcheck for the specific behaviour you need, or catch the exception at the use site
mypy reports errors the code never hitshints describe intent; runtime never enforces themtreat mypy as a linter: fix the annotation or the code, then re-run
int('3.5') raises ValueErrorint() does not parse float-formatted stringsconvert via float() first, then int(), when fractional strings are valid input
mutable default argument keeps statedef f(items=[]) evaluates the list onceuse None as the default and create the list inside the function
bool('False') is Trueany non-empty string is truthyparse boolean strings explicitly instead of relying on bool()

Key Results

  • Built conversion helpers that handle 100% of bad input without raising.
  • Annotated functions with type hints validated by mypy.
  • Documented the 5 core scalar types and their conversion pitfalls.
  • Established defensive input patterns reused across later scripts.

Key Takeaways

  • Types live on objects, not names: Python variables are just bindings.
  • Type hints document intent for humans and tools; isinstance() guards enforce it at runtime.
  • Centralize risky conversions in helpers that return a default on failure.
  • Know mutability: aliasing surprises come from shared mutable objects.
  • Test helpers with both good and bad input before trusting them in automation.

Practice Challenges

Challenge 1: Strict parser

Goal: Extend to_int into to_number that accepts int strings, float strings, and returns the default for anything else, with type hints.

Hint: Try float(s) as a fallback before giving up; keep the same default pattern.

Challenge 2: Truthy trap

Goal: Write to_bool that maps 'true'/'yes'/'1' to True and 'false'/'no'/'0' to False case-insensitively, raising on anything else.

Hint: Normalize with s.strip().lower() first, then compare against explicit sets.