| name | cascade-type-conversion |
| description | For flexible parsing: try multiple type conversions in order, graceful fallback from specific to general types. |
cascade-type-conversion
When to Use
- Parsing mixed-type data
- User input that could be various types
- "Best effort" type inference
- Atom/literal parsing in interpreters
When NOT to Use
- Known fixed types
- Type ambiguity is a bug, not a feature
- Performance-critical inner loops
The Pattern
Try conversions from most specific to most general, catching failures.
def parse_value(s):
"""Parse string as most specific type possible."""
try:
return int(s)
except ValueError:
pass
try:
return float(s)
except ValueError:
pass
if s.lower() in ('true', 'false'):
return s.lower() == 'true'
return s
Example (from pytudes)
def atom(token):
"""Numbers become numbers; #t/#f are booleans; strings stay strings."""
if token == '#t':
return True
elif token == '#f':
return False
elif token[0] == '"':
return token[1:-1]
try:
return int(token)
except ValueError:
try:
return float(token)
except ValueError:
try:
return complex(token.replace('i', 'j', 1))
except ValueError:
return Sym(token)
def atom(token):
"""Numbers become numbers; every other token is a symbol."""
try:
return int(token)
except ValueError:
try:
return float(token)
except ValueError:
return Symbol(token)
def atom(text: str):
"""Parse text into a single float or int or str."""
try:
x = float(text)
return round(x) if x.is_integer() else x
except ValueError:
return text.strip()
try:
from math import gcd
except ImportError:
from fractions import gcd
Key Principles
- Specific to general: Try int before float before string
- Catch ValueError: Standard exception for failed conversions
- Chain try/except: Each failure falls through to next
- Final fallback: Always have a catch-all (usually string)
- Return in try block: Conversion succeeded, return immediately