Page 2 of 8~96 min topic

Python only what you need

Define the score labeler input contract

Page 2 hardens the boundary around the threshold score labeler (`scores.py`) so bad inputs fail before the interesting algorithm runs.

~12 min this pageData contractReviewed 2026-08-08

1Learn the idea

Read

Define what may enter

The accepted input remains: a list of numeric scores in 0..1 and a threshold in 0..1. Keep parsing and normalization in functions that do not score, train, or call a model. That split lets a test fail the boundary without blaming the core logic. The user-facing decision stays: accept or reject a model score using one shared cutoff.

Read

Reject at the boundary

def require_number(value, name):
    if isinstance(value, bool) or not isinstance(value, (int, float)):
        raise TypeError(f"{name} must be a number")
    value = float(value)
    if not 0.0 <= value <= 1.0:
        raise ValueError(f"{name} must be between 0 and 1")
    return value

def require_scores(values):
    if not isinstance(values, list):
        raise TypeError("scores must be a list")
    out = []
    for index, value in enumerate(values):
        out.append(require_number(value, f"score[{index}]"))
    return out

def validate(scores, threshold):
    return require_scores(scores), require_number(threshold, "threshold")

print(validate([0.2, 0.9, 0.4], 0.5))

Expected output:

([0.2, 0.9, 0.4], 0.5)

This contract intentionally rejects numeric strings. Silently accepting "0.9" in one place but comparing strings elsewhere creates inconsistent behavior. It also rejects booleans: Python treats True as the integer 1, but a truth value is not a score in this contract.

Negative tests:

for scores, threshold in [([0.2, "0.9"], 0.5), ([True], 0.5), ([1.2], 0.5), ([0.2], -0.1)]:
    try:
        validate(scores, threshold)
    except (TypeError, ValueError) as error:
        print(type(error).__name__, error)

Each case must print a field-named error; none should reach labeling.

Read

Keep transforms testable

Write one assertion for a neighboring valid input to the score labeler so tightening the boundary does not over-reject. Document field names and types the way a teammate would need them on day two of python-only-what-you-need—not as comments you plan to delete.

Read

Lab notebook: name the fields

List every field in scores=[0.2,0.9,0.4], threshold=0.5 and mark each as required, optional, or forbidden. Required fields must fail loudly when missing; optional fields need defaults you can quote in a test; forbidden fields (secrets, raw PII, path escapes) must never be accepted silently. This list is the contract for the score labeler.

Add one sentence about encoding, units, or timezones if relevant to a list of numeric scores in 0..1 and a threshold in 0..1. Contracts that ignore units create “correct” programs that still ship wrong decisions when someone tries to accept or reject a model score using one shared cutoff.

Read

Worked judgment

Write the error string you want for the most likely bad input. Prefer ValueError('threshold out of range')-style messages over generic invalid input. The contract’s job is to make string scores that compare lexicographically, or IndentationError that hides a wrong cutoff harder to confuse with a model or algorithm bug later.

Read

Independent transfer

Adapt the contract for integer percentages from 0 through 100. Decide whether 90.0, "90", and True are accepted, then write one valid-neighbor test beside every rejection.

ML Python starter

Previous · Next

Go deeper

Before you start

Why this matters

Invent one malformed input that the threshold score labeler (scores.py) might accidentally accept. Predict the exception or rejection message. After you run the contract code, compare your prediction with the real failure text.

In the wild

See how this idea shows up as a product and a company — then come back to the lesson. Skills transfer across vendors.

Check your understanding

Page assessment

Answer from memory. Completion is saved from this evidence, not from opening the next page.

1. Which malformed values die before core logic?
2. Can transform and prediction/search be tested separately?
3. Does the error name the violated field or shape?
4. Is the accepted input still exactly: a list of numeric scores in 0..1 and a threshold in 0..1?

All responses are required.