Quick summary
Summarize this blog with AI
If payload['scores'][0] raises TypeError: 'float' object is not subscriptable, the immediate operation is wrong because payload['scores'] is one number. In an API or dictionary pipeline, the deeper question is why a field expected to contain a list became a scalar.
This guide follows that upstream contract problem. For a quick explanation of indexing a scalar, use the general float-indexing guide. The examples here are synthetic in-memory payloads, tested with Python 3.12.14; they make no network requests.
A dictionary default does not change an existing value
payload = {'scores': 9.7}
scores = payload.get('scores', [])
print(type(scores).__name__)
try:
print(scores[0])
except TypeError as exc:
print(str(exc))
Expected output:
float
'float' object is not subscriptable
The empty-list default applies only if the key is absent. It does not wrap the existing float in a list or validate its type. Replacing the failure with str(scores)[0] changes the task to reading a character. Wrapping every value in a list can hide a schema change instead of correcting it.
Inspect the value at each boundary
import json
payload = json.loads('{"result": {"scores": 9.7}}')
result = payload['result']
print(type(payload).__name__)
print(type(result).__name__)
print(type(result['scores']).__name__)
Expected output:
dict
dict
float
The JSON parser has correctly decoded an object containing a numeric scalar. A parser cannot infer that the producer intended an array. Compare the actual field shape with the API specification or producer's return contract. In production diagnostics, log field paths and types rather than complete private payloads.
Validate a list-of-scores contract explicitly
This example requires a result object with a nonempty scores list. Each score must be a finite built-in integer or float between 0 and 100. Booleans are rejected despite being integer subclasses. The validator returns a fresh list, so the consumer cannot mutate the producer's list by accident.
import math
def read_scores(payload):
if not isinstance(payload, dict):
raise TypeError('payload must be an object')
result = payload.get('result')
if not isinstance(result, dict):
raise TypeError('result must be an object')
if 'scores' not in result:
raise ValueError('result.scores is required')
scores = result['scores']
if not isinstance(scores, list):
raise TypeError('result.scores must be a list')
if not scores:
raise ValueError('result.scores must not be empty')
for index, score in enumerate(scores):
if type(score) not in (int, float):
raise TypeError(f'result.scores[{index}] must be a number')
if not 0 <= score <= 100 or not math.isfinite(score):
raise ValueError(f'result.scores[{index}] must be finite and in range')
return scores.copy()
print(read_scores({'result': {'scores': [9.7, 10]}}))
for payload in (
{'result': {'scores': 9.7}},
{'result': {}},
{'result': {'scores': []}},
{'result': {'scores': [True]}},
):
try:
read_scores(payload)
except (TypeError, ValueError) as exc:
print(type(exc).__name__ + ': ' + str(exc))
Expected output:
[9.7, 10]
TypeError: result.scores must be a list
ValueError: result.scores is required
ValueError: result.scores must not be empty
TypeError: result.scores[0] must be a number
The bounds check also rejects huge integers before a float-range conversion is attempted. If the real API permits scalar scores, optional values, or an empty list, define those cases explicitly instead of copying this teaching policy unchanged.
Keep function return shapes consistent
def summarize_scores(scores):
if not scores:
raise ValueError('at least one score is required')
return {'count': len(scores), 'mean': sum(scores) / len(scores)}
scores = read_scores({'result': {'scores': [10, 20]}})
summary = summarize_scores(scores)
print(summary)
print(summary['mean'])
Expected output:
{'count': 2, 'mean': 15.0}
15.0
This summary always returns a dictionary on success; its mean field is always a scalar. Returning a float on one branch and a record on another makes the caller's indexing assumption fragile. Type annotations and static checks help document the expected shape, but runtime data from a remote boundary still needs validation.
Test the producer and consumer together
Include a valid list, a scalar replacing the list, a missing field, an empty list, null, a boolean, and an out-of-range value. When a failure occurs, keep the traceback and identify the earliest boundary where the contract changes. Repair that boundary or update the consumer deliberately; do not catch TypeError and substitute a fabricated zero score.
This URL remains available for the API-contract problem. For DataFrame aggregation that intentionally returns a scalar, use the pandas scalar-shape guide. Primary references: JSON decoding types and dictionary get.