Python Syntax & Error Handling Guide for Data Scientists

CAREER Published Updated 4 mins read By Leon Wei
Python Syntax & Error Handling Guide for Data Scientists cover image

Quick summary

Summarize this blog with AI

A useful Python syntax and error-handling guide should make failures reproducible. This lesson uses small, self-contained examples to separate syntax errors, type errors, bad values, and division by zero. It then builds a row validator that reports rejected data without hiding programming mistakes.

Tested with Python 3.12.14. Run the blocks in order; each example defines its own input. No downloads, plotting libraries, or external files are required.

Start with a clear scalar or collection contract

Indentation defines a block. A function returns a value only when execution reaches a return. A comprehension builds a collection, while arithmetic on a selected element produces a scalar.

def double_nonnegative(values):
    return [value * 2 for value in values if value >= 0]

measurements = [-2, 0, 3]
doubled = double_nonnegative(measurements)
print(doubled)
print(measurements)

Expected output:

[0, 6]
[-2, 0, 3]

The original list stays unchanged. Names such as measurements and doubled make the collection role clearer than reusing x for unrelated scalars and lists in consecutive examples.

Distinguish the error before choosing a handler

A syntax error prevents a source block from running. A runtime error happens after parsing succeeds. This example compiles a deliberately invalid string so the surrounding diagnostic code can keep running.

try:
    compile('if True print("hello")', '<example>', 'exec')
except SyntaxError as exc:
    print(type(exc).__name__)

cases = [
    ('wrong type', lambda: [1, 2] / [3, 4]),
    ('bad value', lambda: float('missing')),
    ('zero denominator', lambda: 10 / 0),
    ('missing position', lambda: [10, 20][5]),
]
for label, operation in cases:
    try:
        operation()
    except (TypeError, ValueError, ZeroDivisionError, IndexError) as exc:
        print(label + ': ' + type(exc).__name__)

Expected output:

SyntaxError
wrong type: TypeError
bad value: ValueError
zero denominator: ZeroDivisionError
missing position: IndexError

That multi-exception handler exists only to compare a known set of intentional failures. In application code, catch the specific failure you can handle. A handler for ZeroDivisionError cannot repair list division: dividing Python lists raises TypeError before any numeric denominator is considered.

Keep a recoverable operation inside a narrow try block

This function expects two numeric strings and returns a finite ratio. Invalid text raises ValueError, non-text input raises TypeError, and a zero denominator raises ZeroDivisionError. The caller supplies the user-facing recovery behavior.

import math

def parse_ratio(numerator_text, denominator_text):
    if not isinstance(numerator_text, str) or not isinstance(denominator_text, str):
        raise TypeError('both inputs must be text')
    numerator = float(numerator_text)
    denominator = float(denominator_text)
    if not math.isfinite(numerator) or not math.isfinite(denominator):
        raise ValueError('inputs must be finite')
    if denominator == 0:
        raise ZeroDivisionError('denominator must not be zero')
    result = numerator / denominator
    if not math.isfinite(result):
        raise ValueError('ratio is outside the finite numeric range')
    return result

for numerator_text, denominator_text in [('10', '2'), ('10', '0'), ('bad', '2')]:
    try:
        ratio = parse_ratio(numerator_text, denominator_text)
    except ValueError:
        print('Invalid numeric value')
    except ZeroDivisionError:
        print('Denominator cannot be zero')
    else:
        print(ratio)

Expected output:

5.0
Denominator cannot be zero
Invalid numeric value

else runs only if the try block succeeds. finally is for cleanup that must run regardless of success; it is not a place to return a fallback value that suppresses an exception. When a resource supports it, a with statement usually expresses cleanup more clearly.

Define the validator before using it

A data-quality rule needs an actual implementation. Here a row must be a dictionary with a nonempty text ID and a text score representing a finite number from 0 through 100. Expected data failures become RowValidationError; an unexpected non-dictionary row remains a programming contract error.

class RowValidationError(ValueError):
    pass

def validate_row(row):
    if not isinstance(row, dict):
        raise TypeError('row must be a dictionary')
    row_id = row.get('id')
    if not isinstance(row_id, str) or not row_id.strip():
        raise RowValidationError('id must be nonempty text')
    raw_score = row.get('score')
    if not isinstance(raw_score, str):
        raise RowValidationError('score must be text')
    try:
        score = float(raw_score)
    except ValueError as exc:
        raise RowValidationError('score must be numeric') from exc
    if not math.isfinite(score) or not 0 <= score <= 100:
        raise RowValidationError('score must be finite and between 0 and 100')
    return {'id': row_id.strip(), 'score': score}

rows = [
    {'id': ' a ', 'score': '85'},
    {'id': 'b', 'score': 'unknown'},
    {'id': 'c', 'score': '120'},
]
accepted, rejected = [], []
for row_number, row in enumerate(rows, start=1):
    try:
        cleaned = validate_row(row)
    except RowValidationError as exc:
        rejected.append((row_number, str(exc)))
    else:
        accepted.append(cleaned)

print(accepted)
print(rejected)

Expected output:

[{'id': 'a', 'score': 85.0}]
[(2, 'score must be numeric'), (3, 'score must be finite and between 0 and 100')]

raise ... from exc preserves the original conversion error as the cause. The accepted/rejected split makes data loss visible. Do not put a blanket except Exception: pass around the entire pipeline: a misspelled name or broken function should still be discoverable.

Test boundaries and keep failures observable

Test empty rows, missing keys, whitespace-only IDs, NaN, infinity, and values exactly at 0 and 100. Decide whether the job rejects a row or the whole batch, and record counts so an empty result cannot look like a successful analysis. Keep identifiers or row numbers in error reports without exposing private record contents.

Use the loops and functions lesson for normalization and outlier calculations, or the pandas cleaning lesson for a complete DataFrame workflow. Python's errors and exceptions tutorial and exception reference describe the language behavior used here.

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles