Pebellslearn by building
0%
RoadmapMatrixCatalogSandboxSign in
Fundamentals  ›  Lesson

Type Casting & Input

Type Casting 13 minConverting between text and numbers
You're building a piece ofBill Splitter & Tip Calculator
This piece — parse_amount(): Turns messy text guests type ($1,234.50) into real numbers.
Scenario A guest types "$1,234.50" into the bill field. The app gets text and must convert it to a number to do the math.
Your task
Build parse_amount(text). It cleans a price string and returns it as a float. Example: parse_amount("$84.00") → 84.0.

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().

python

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") to int("42.0"). It raises ValueErrorint() 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 an int, a float, or a str.

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.

python

// 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().

python

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") return NaN like JS Number("abc")?" No — Python raises ValueError instead of a silent NaN. That's deliberate: bad input fails loudly at the cast, not three functions later. There's no NaN-style poison value to thread through your code.

python

One of these looks like it should obviously work, and doesn't:

What happens when you run this?
print(int("3.5"))

The lines below would each raise ValueError — this demo catches them so you can see exactly which inputs blow up:

python

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).

python

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 returns 42.0 — remember, float() tolerates the surrounding spaces even inside the try.

Watch out: Catch the specific exception (except ValueError), not a bare except:. A bare except also swallows typos and KeyboardInterrupt, 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 it

We 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:

python

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 whatever input() 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.

python

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" + 1 concatenate like JavaScript?" No — Python does no implicit coercion. "a" + 1 raises TypeError; cast first ("a" + str(1)). You never get JS-style surprises like [] + {} or "5" - 1; mixing types is an error, not a silent conversion.

python

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.

python

.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:

python

Try it: Add another .replace(" ", "") before the float(...) — 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 $ and float() raises the ValueError from 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

python

Coming from Java/JS: f"{x:,.2f}" is Python's String.format("%,.2f", x) / JS x.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%) — no Intl/.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:

python

Real-world — a column-aligned receipt (a fill char goes before the align):

python

Handy: f"{value!r}" shows the repr() (quotes and all — great for debugging strings), and f"{total=}" (3.8+) prints total=1234.5 — the name and value in one go.

python

🎯 Your turn

Write parse_amount(text) — it cleans a price string and returns it as a float:

  • parse_amount("$84.00")84.0
  • parse_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. ✅

Practice — level up

0/5 solved

Solve each in its own editor — Run to try, Check to grade. Stuck? Reveal a Hint, or the full Solution + walkthrough.

Warm-upSafe int

Write safe_int(text) returning int(text), or 0 if it isn't a valid whole number.

casttry/except
DrillMoney format

Write money(amount) returning a string like "$1,234.50" — a leading $, thousands separators, exactly 2 decimals.

f-strings
BuildParse a CSV row

Write parse_row(row) for a "name,price,qty" string, returning a list [name, price(float), qty(int)], or None if it doesn't have 3 parts or the numbers are invalid.

splitcasttry/except
BossAligned receipt

Write format_receipt(items) where items is [(name, price), ...]. Return a multi-line string: each line name left-aligned in 12 cols then $price right-aligned in 7 cols (2 decimals), and a final TOTAL line the same way. Join with \n.

comprehensionf-string-alignsum
CapstoneClean messy amounts

Write clean_amounts(raw) for a list of messy price strings (may contain $, ,, spaces, or junk). Return {"total": <sum of valid, 2dp>, "valid": <count>, "skipped": <count of unparseable>}.

casttry/exceptloopdictround
parse_amount.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
parse_amount(text) → floatClean a price string and return it as a float.
try:
returned
Press Run to execute your code.
Press Check to run the tests for this lesson.