Type Casting & Input
Our Bill Splitter lives on the boundary between text (what a guest types into a field) and numbers (what we need to do the math). Casting is how we cross that boundary — turning "$1,234.50" into 1234.5 so we can split it, and turning 21.0 back into text to display it. Get this right and every downstream calculation just works.
Every box is live — edit and ▶ Run.
Part 1 — The four casting constructors
Python's core types double as constructors: call the type like a function and it tries to build that type from what you pass. You'll reach for these constantly: int(), float(), str(), and bool().
Notice int("42") reads text and gives a whole number, while str(42) goes the other way. int(3.9) on a float chops off the decimal part (more on that in Part 3), and str(...) lets you glue a number into a message.
Try it: Change
int("42")toint("42.0"). It raisesValueError—int()on a string wants a whole-number string, not a decimal one. We'll tame that in Part 4.
Note: These are the everyday four. There are more (
list(),tuple(),set(),dict(),complex()), but for a restaurant bill you almost always want anint, afloat, or astr.
Part 2 — Recap: / always gives you a float
When you split a bill, you divide. In Python 3 the single-slash / always produces a float, even when the numbers divide evenly. That's usually exactly what you want for money.
// is floor division (whole-number result) and % is the remainder — handy when you split leftover cents. But the plain / you'll use for per-person shares hands back 21.0, not 21.
Note: Because
/yields a float, you rarely need to cast the result of a division. You cast the inputs — the strings from the form — before they ever reach the math.
Part 3 — int() truncates, round() rounds
This trips up a lot of people. Casting a float with int() does not round — it truncates (drops everything after the decimal point, toward zero). When you actually want the nearest whole number, use round().
Two real-world gotchas hide here. First, round(2.5) is 2, not 3: Python uses banker's rounding (round half to even) to avoid statistical bias. Second, round(2.675, 2) gives 2.67, not 2.68, because 2.675 can't be stored exactly in binary float.
Watch out: Never store money as a float you round for display and hope for the best. For a tip calculator, rounding to 2 decimals for display is fine, but if you were building real accounting you'd reach for
decimal.Decimal. For this app,round(total / people, 2)is the right tool for a clean per-person number.
Part 4 — Casting can fail: ValueError
int() and float() are strict. Hand them text they can't interpret as a number and they raise a ValueError — which crashes the program if nobody catches it. Good news: float() is forgiving about surrounding whitespace.
❓ Cross-question — "Does
int("abc")returnNaNlike JSNumber("abc")?" No — Python raisesValueErrorinstead of a silentNaN. That's deliberate: bad input fails loudly at the cast, not three functions later. There's noNaN-style poison value to thread through your code.
One of these looks like it should obviously work, and doesn't:
print(int("3.5"))The lines below would each raise ValueError — this demo catches them so you can see exactly which inputs blow up:
So float("3.5") is fine (it's a valid decimal), but float("abc") and float("") fail. Meanwhile int("3.5") fails too, because int() on a string refuses decimals. The lesson: a $ sign, a comma, or a stray letter is enough to crash a naive cast.
Note:
float()strips leading/trailing whitespace for you, which is why" 3.14 "works. It does not strip currency symbols or thousands commas — that's the cleaning job in Part 8.
Part 5 — Guarding a cast with try/except
In a real app you can't let one bad keystroke crash the whole checkout. Wrap the risky cast in try/except ValueError and return a safe fallback (or ask the user to retry).
Here to_float never explodes: good input becomes a number, bad input becomes None, and the caller decides what to do with None (show an error, default to 0, etc.).
Try it: Add
print(to_float(" 42 ")). It returns42.0— remember,float()tolerates the surrounding spaces even inside thetry.
Watch out: Catch the specific exception (
except ValueError), not a bareexcept:. A bare except also swallows typos andKeyboardInterrupt, hiding real bugs.
Part 6 — input() always returns a str
When a script reads from the keyboard with input(), it always returns a string — even if the user typed digits. Forgetting to cast is the single most common beginner bug.
age = input("Age? ") # user types 18 ...
if age > 18: # 💥 TypeError: comparing str to int
...
age = int(input("Age? ")) # ✅ cast the moment you read itWe can't pop up a real prompt inside this playground, so the demo below pretends input() handed us "18" and shows why you must cast:
Note: In this playground the widgets and try chips supply values for you, so you rarely call
input()directly. But you'll see it in every real script — treat whateverinput()returns as text and cast it immediately.
Part 7 — bool() and truthiness
bool() answers "is this value truthy?" You'll lean on this for validation: did the guest actually type something? The falsy values are worth memorizing — 0, 0.0, "" (empty string), [] (empty list), {}, and None. Almost everything else is truthy.
Look hard at the last line: bool("0") is True. The string "0" is a non-empty string, so it's truthy — even though the number 0 is falsy. This is a classic bill-field bug: if a guest types "0", the field is non-empty (truthy) but the amount is zero.
❓ Cross-question — "So does
"a" + 1concatenate like JavaScript?" No — Python does no implicit coercion."a" + 1raisesTypeError; cast first ("a" + str(1)). You never get JS-style surprises like[] + {}or"5" - 1; mixing types is an error, not a silent conversion.
Watch out:
if field:checks whether the text box is empty, not whether the number is zero. To reject a zero bill you must cast first and test the number:if float(field) == 0. Text-truthiness and number-value are two different questions.
Part 8 — Clean the text, then cast
Real price fields arrive messy: " $1,234.50 " has spaces, a dollar sign, and a thousands comma — all three make float() choke. The fix is to clean the string first with .strip() and .replace(), then cast. Each string method returns a new string, so you can chain them.
.strip() removes surrounding whitespace, .replace("$", "") deletes the dollar sign, and .replace(",", "") deletes the comma. What's left — "1234.50" — is a clean numeric string float() accepts. That exact chain is the whole idea behind parse_amount(text), the function you're about to write:
Try it: Add another
.replace(" ", "")before thefloat(...)— it would also strip internal spaces like"1 234". For this lesson.strip()is enough, but it shows how cleaning steps stack up.
Note: Order doesn't matter much here since the replacements target different characters, but always clean before you cast. One leftover
$andfloat()raises theValueErrorfrom Part 4.
Part 9 — Formatting numbers with f-strings
Casting a number back to text for display is where f-strings shine. Put a colon after the value inside the braces and add a format spec — this is how you show money, percentages, and neatly aligned columns.
Decimals, thousands separators, percents
Coming from Java/JS:
f"{x:,.2f}"is Python'sString.format("%,.2f", x)/ JSx.toLocaleString()/x.toFixed(2)— but baked into the string literal.
❓ Cross-question — "Are f-strings just template literals?" Yes, with more power.
f"{x}"≈`${x}`, and the format spec after the:is built in:f"{n:.2f}"≈n.toFixed(2), plus alignment (:>8), thousands (:,), and percent (:.1%) — noIntl/.toFixed/padding helpers needed.
Width, alignment, and padding — for tables
Inside the spec: < left-align, > right-align, ^ center; a number sets the
width; a leading 0 zero-pads:
Real-world — a column-aligned receipt (a fill char goes before the align):
Handy:
f"{value!r}"shows therepr()(quotes and all — great for debugging strings), andf"{total=}"(3.8+) printstotal=1234.5— the name and value in one go.
🎯 Your turn
Write parse_amount(text) — it cleans a price string and returns it as a float:
parse_amount("$84.00")→84.0parse_amount("$1,234.50")→1234.5
Hint — text.strip().replace("$", "").replace(",", ""), then wrap in float(...).
Then press ▶ Run, tap the Live App try chips to call it with different inputs, and hit ✓ Check. Green = this piece of the app is built. ✅
