- What does the code print?
Answer: **Cathe**
When you see an f-string with format specifiers like {name:*^9.5}, break it into three parts: the fill character and alignment (*^), the total width (9), and the precision (.5).
The precision .5 applies first — since name is a string, it truncates it to 5 characters: "Cathe". Then the alignment kicks in: ^ means center-aligned within a total width of 9. "Cathe" is 5 characters, leaving 4 spaces to fill. Centering distributes them as evenly as possible — 2 on each side — and the fill character * replaces those spaces. The result is **Cathe**, making A correct.
B is wrong because it ignores both the precision truncation and the fill formatting entirely, as if no format spec existed. C (Cathe****) reflects a misunderstanding of the alignment — ^ centers, not left-aligns. Left alignment (<) would pad only on the right. D (*Catherine*) makes two mistakes: it ignores the .5 truncation (keeping the full name) and misapplies the padding count, as if only one * goes on each side.
A helpful tip: always process f-string format specifiers in order — precision first, then width and alignment. Students often forget that .5 on a string truncates (unlike on a float, where it controls decimal places). A quick mnemonic: for strings, think of precision as a "character limit." Knowing this order will save you on any question involving combined format specs.
- What is printed by the code?
Answer: | 12.35|
When you see an f-string with nested format specifiers like {value:{width}.{precision}f}, think about three things: the total field width, the decimal precision, and the alignment.
Here's how Python evaluates this: width = 9 means the output occupies 9 total characters, precision = 2 means 2 decimal places, and f means fixed-point notation. Formatting 12.3456 to 2 decimal places gives 12.35 — that's 5 characters. Since the default alignment for numbers is right-aligned, Python pads the remaining 4 spaces on the left, producing | 12.35|. That confirms C is correct.
Walking through the wrong answers: A (|12.345600|) ignores the precision specifier entirely and instead seems to be padding with zeros after the full decimal — this would require a 0 fill character and a different format entirely. B (|12.35 |) gets the rounding right but uses left-alignment instead of right. Left-alignment would require an explicit < flag, like {value:{width}<.{precision}f}. D (| 12.346|) looks like someone applied 3 decimal places instead of 2, then right-aligned — a precision-reading mistake.
A helpful tip: when you see {value:{width}.{precision}f}, mentally evaluate it in two steps — first apply precision to get the formatted number, then apply width with default right-alignment. Remember that Python right-aligns numbers by default, so padding goes on the left unless you specify otherwise with < or ^.
- What is printed by the code?
Answer: | 8.8%|
When working with Python f-string format specifiers, you need to parse each component carefully. The format {value:>7.1%} breaks down into three parts: >7 means right-align within a total width of 7 characters, .1 means round to 1 decimal place, and % means multiply by 100 and append a percent sign.
Start with the math: 0.0875 × 100 = 8.75, which rounds to one decimal place as 8.8. Adding the % symbol gives 8.8%, which is 4 characters long. To fill a total width of 7, Python pads 3 spaces to the left (since > means right-align), producing | 8.8%|. That makes A the correct answer.
Choice B (| 8.7%|) reflects a rounding mistake — truncating toward 8.7 instead of rounding 8.75 up to 8.8. Python follows standard rounding rules (banker's rounding for .5 cases, but 8.75 rounds clearly to 8.8). Choice C (| 0.1%|) would result from forgetting that % multiplies by 100 first — if you naively apply .1% to 0.0875 without the multiplication, you might think the output is near zero. Choice D (|8.8% |) represents a left-alignment error; > means right-align, so spaces go on the left, not the right. Left-alignment would use < instead.
A useful memory trick: in Python format specs, think fill → align → width → precision → type in that order. When you see % as the type, always remember it multiplies by 100 automatically before applying precision.
- What does the code print?
Answer: { Ada }
When you see an f-string with curly braces, your first job is to carefully count and categorize every brace: {{ and }} are escape sequences that produce literal { and } characters, while single {...} triggers expression evaluation and formatting.
Let's parse f"{{{name:^7}}}" piece by piece. The opening {{ produces a literal {. Then {name:^7} evaluates the variable name ("Ada") with center-alignment (^) in a field of width 7 — that's 7 characters total, so "Ada" (3 chars) gets 2 spaces on each side: Ada . Finally, }} produces a literal }. Combining everything: { Ada }, which is answer D.
Now let's examine the wrong answers. A shows { Ada } — this would result from left- or right-aligned padding, not center alignment (^). It also has incorrect spacing. B shows {Ada } — this ignores both the center-alignment formatting and the outer literal braces entirely, as if only the variable name were printed. C shows {{ Ada }} — this is a common trap: a student might think {{ and }} add to the braces rather than replace them, doubling up the curly characters in the output. But {{ always collapses into a single {.
Study tip: When analyzing f-strings with escaped braces, mentally process them in two passes — first replace all {{ → { and }} → }, then evaluate any remaining {expression} blocks with their format specs. This two-pass approach prevents brace-counting errors.
- Which statement prints exactly
Item: ALPHA (007)? Answer: print(f"Item: {data['name'].upper()} ({data['qty']:03d})")
When working with Python f-strings, you often need to combine string methods and format specifiers in the same expression. Questions like this test whether you can read both pieces correctly and predict the exact output.
The target output is Item: ALPHA (007). This tells you two things: the name must be fully uppercased, and the quantity must be zero-padded to exactly three digits. In an f-string, {data['name'].upper()} calls the .upper() method on the string "alpha", producing "ALPHA". For the number, the format specifier :03d means: integer (d), minimum width of 3, padded with zeros — so 7 becomes "007". Option A combines both of these correctly, making it the right answer.
Option B is wrong because it omits .upper(), so data['name'] prints as "alpha" (lowercase), not "ALPHA". Option C applies .upper() correctly but uses :02d instead of :03d — a width of 2 means 7 becomes "07", not "007". Option D uses :3d, which gives a width of 3 but pads with spaces, not zeros, producing " 7" rather than "007".
A handy tip: when you see a zero-padding format specifier like :03d, break it into three parts — 0 (pad character), 3 (minimum width), d (integer type). If the 0 is missing, Python defaults to space-padding. On exam questions, the difference between :3d and :03d is a classic trap, so always check whether the zero is explicitly present.
- What is printed by the code?
Answer: **3**|0005
When you see Python f-strings with format specs, break each field apart at the | and analyze them separately.
The first field is {x:*^5}. Here, * is the fill character, ^ means center-align, and 5 is the total width. Since x = 3, you're centering the single character "3" in a 5-character field padded with asterisks. One character needs 4 padding characters, split evenly: 2 on the left, 2 on the right — giving you **3**.
The second field is {x + 2:04d}. First, x + 2 = 5. The format spec 04d means: integer (d), total width of 4, zero-padded. The number 5 is one digit, so three zeros are prepended: 0005.
Combining both with the literal | separator gives you **3**|0005, which is A.
Choice B (**3**|5000) reverses the zero-padding to the right — zero-padding always fills on the left to preserve the numeric value. Choice C (*3***|0005) would result from left-aligning (<) instead of center-aligning (^), but the spec uses ^. Choice D (**3**|00005) adds an extra zero, suggesting someone mistakenly used a width of 5 instead of 4 for the second field — the spec clearly says 04d, so total width is 4.
A handy tip: read format specs left to right as "fill character → alignment → width." For numeric specs, the digit before the type letter is always the total field width, including the number itself.
- Which output is produced?
Answer: [- 42]
When you encounter Python f-string format specs, break the format mini-language into individual flags before predicting output. The spec =+7d contains three distinct pieces: = (sign-aware zero-padding alignment), + (force a sign character), and 7 (minimum width of 7).
The = fill alignment is the key here. Normally, padding fills the space before the sign and digits together. But = alignment inserts padding between the sign character and the digits themselves. So for -42 with width 7, Python places the - sign first, then pads with spaces to reach the total width, then appends 42. That gives [- 42] — which is answer B.
Answer A ([ -42]) would be produced by default right-alignment (no = flag), where all padding appears to the left of the sign. Answer C ([+ 42]) would require a positive number — since n = -42, the sign must be -, not +. The + flag means "always show a sign," but it still shows the correct sign for the value. Answer D ([-42 ]) represents left-alignment using the < flag, which places padding to the right of the content — that's not what = does.
A handy mental model: think of = as "equals sign fills the gap between sign and number." It's sometimes called sign-aware padding or zero-padding style (since 0 before width is a shorthand for = with 0 fill). When you see = in a format spec, always ask where the sign lands — it anchors to the far left, with padding filling inward.
- What is printed by the code?
Answer: 2-1-1
When you see f-strings that call methods inside {}, the key is to evaluate each expression in left-to-right order and track how each call mutates the list before the next one runs.
Starting with values = [1, 2], let's walk through the f-string f"{values.pop()}-{values[-1]}-{len(values)}" step by step. First, values.pop() removes and returns the last element — that's 2, so the list becomes [1]. Next, values[-1] accesses the last element of the now-modified list [1], which is 1. Finally, len(values) returns the length of [1], which is 1. Concatenating with dashes gives you "2-1-1", confirming D is correct.
Here's why each wrong answer is tempting but flawed. A (1-1-1) swaps the pop result with the index result — it assumes pop() returns the first element, but Python's pop() with no argument always removes from the end. B (2-2-1) gets the pop result right but evaluates values[-1] against the original list before pop() ran, ignoring that the list was already mutated. C (1-2-2) makes two errors: it reads pop() as returning 1 (the front) and treats len() as if the list is unchanged.
The strategy to remember: method calls inside f-strings execute left to right, and mutations are immediate. If pop() comes first in the string, the list is already shorter by the time the next {} is evaluated. Always mentally step through each {} in sequence, updating the data structure as you go.
- Which output is produced? (Spaces are represented literally.)
Answer: 'A\nB'
When you see an f-string with both a conversion flag and a format spec, you need to apply them in order: conversion first, then formatting.
The !r conversion calls repr() on the value before any width/alignment formatting happens. So repr("A\nB") produces the string 'A\nB' — a 6-character string that includes the surrounding single quotes and the literal backslash-n (since repr() shows the escape sequence rather than the actual newline). Only after this conversion does Python apply the :>8 format spec, which right-aligns the result in a field of width 8. Since 'A\nB' is 6 characters, Python pads 2 spaces on the left, giving 'A\nB' — exactly answer B.
A ( A\nB) is wrong on two counts: it shows 4 spaces of padding and omits the surrounding quotes. This would only make sense if no !r conversion were applied and the raw string (with a literal newline, length 3) were being padded — but even then the newline wouldn't display as \n.
C ('A\nB' ) uses left-alignment (<), not right-alignment. The > in :>8 means right-align, so padding goes on the left, not the right.
D ( 'A\nB') shows 3 spaces of padding instead of 2. The repr'd string 'A\nB' is 6 characters; a width-8 field leaves exactly 2 spaces of padding, not 3.
Study tip: Always mentally evaluate f-string conversions (!r, !s, !a) before the format spec — the format spec operates on the already-converted string's length.
- What is printed by the code?
Answer: 6 15
When working with f-strings in Python, the critical concept to understand is when the string is evaluated. An f-string is evaluated immediately at the moment it's created — it doesn't store a reference to a variable for later use.
In this code, message is assigned f"{x * 3}" when x equals 2, so Python calculates 2 * 3 = 6 right then and stores the string "6" in message. Reassigning x = 5 afterward has no effect on message — that string is already frozen. When print runs, it outputs message (which is "6") followed by f"{x * 3}" evaluated fresh with x = 5, giving 5 * 3 = 15. The result is 6 15, making C correct.
A (6 6) assumes that both values use x = 2, as if the second f-string also captured the original value — but by the time print runs, x has already been updated to 5. B (15 15) makes the opposite mistake: assuming message somehow "updates" when x changes, treating it like a live formula rather than a fixed string. D (2 5) would only make sense if f-strings printed the raw variable rather than the computed expression — they evaluate the entire expression inside {}, not just the variable name.
The key takeaway: f-strings are not templates that re-evaluate later — they produce a plain string immediately. Whenever you see a variable reassigned after an f-string is created, remember that the f-string is already done computing and won't change.