Material

Python practice: code, objects and concurrency

These independent assignments use synthetic data. Try them first, then check edge cases and open the hint. Time is an estimate; allow additional time for project tests and documentation.

Choose your practice · Interview mistakes

Scroll the table horizontally →

Assignment Stage Estimate
Two guests, separate preferences Python basics 25 min
Repeated order events Python basics 25 min
The largest order without hidden losses Python basics 20 min
A seating plan without shared rows Python basics 25 min
Find a dish by a short code Python basics 25 min
Clean a kitchen note Python basics 25 min
Compress display signals Python basics 30 min
Reverse an allergen directory Python basics 25 min
A menu item in a set Python engineering 30 min
Container packs for an order Python engineering 40 min
A diamond in a handler hierarchy Python engineering 30 min
Register notification types Python engineering 35 min
Each callback sends its own status Python engineering 25 min
Rotate kitchen shifts Python basics 25 min
Stream receipt-line totals Python engineering 35 min
Audit a function result Python engineering 30 min
Publish a report atomically Python engineering 40 min
Collect supplier prices asynchronously Async and background jobs 50 min
A ticket checksum Python basics 25 min
Count tasting-menu combinations Python engineering 35 min

Two guests, separate preferences

A café stores the template {"allergens": ["nuts"], "alerts": {"email": True}}. Implement new_preferences(template). Create preferences for two guests; add milk and disable email for the first guest. The second guest and the template must keep their original values. Then investigate a function with allergens=[]: why can two calls share a list? Demonstrate == and is on your objects.

Check your result

  • Nested lists and dictionaries are independent; test each level.
  • Cover an empty list, repeated calls and a list inside a tuple separately.
  • Explain why copying the outer dictionary does not copy its nested objects.
Hint — after your attempt

Draw which variables refer to the same object.

Extension: Add optional preferences without a mutable default argument.

Back to this stage

Repeated order events

A scanner sent [0, 14, 14, 7, 0, 19, 7]. Implement unique_order_ids(ids): preserve first-occurrence order and return [0, 14, 7, 19]. Zero is a valid identifier. Do not mutate the input list. Explain runtime and extra memory for a large stream.

Check your result

  • Empty input produces an empty result.
  • Handle duplicates and 0 without sorting or using truthiness as membership.
  • Measure runtime growth for 10,000 and 100,000 items.
Hint — after your attempt

Separate output order from the membership check.

Extension: Accept any finite iterable, including a one-shot generator.

Back to this stage

The largest order without hidden losses

An order report contains integer minor-unit amounts: [0, -250, 980, 980, 410]. Return the first position of the maximum: (2, 980). Negative amounts represent refunds and are valid. Return None for empty input; for bool values and strings, raise ValueError. Next, find pairs of distinct positions whose amounts differ by at least a given threshold; explicitly distinguish position pairs from unique value pairs.

Check your result

  • Handle all-negative input and tied maxima.
  • For [10, 10, 40] and threshold 30, there are two position pairs and one value pair.
  • Reject a negative threshold and explain pair-search complexity.
Hint — after your attempt

Initializing the maximum to zero fails for a list of refunds.

Extension: Compare nested loops with a sorted approach, including sorting cost.

Back to this stage

A seating plan without shared rows

Build a rectangular seating grid rows × cols, numbered row by row from start. For (2, 3, 11), return [[11, 12, 13], [14, 15, 16]], row sums [36, 45] and column sums [25, 27, 29]. Dimensions must be positive integers. Explain what happens when constructing the grid by repeating one list; test it by replacing a single seat.

Check your result

  • Numbers are unique and consecutive.
  • Replacing a cell does not affect other rows.
  • Cover 1×1, 1×4 and invalid dimensions.
Hint — after your attempt

Check whether grid rows are the same object.

Extension: Accept unavailable seat numbers and sum only available seats.

Back to this stage

Find a dish by a short code

A register matches a code as a subsequence of a dish name: characters must appear in order, with gaps allowed. Use ASCII letters only and ignore case. matches("tea", "steak") is True; matches("tea", "eat") is False. An empty code matches any name. Implement the check and explain why comparing sets of characters is insufficient.

Check your result

  • Repeated letters retain their count and order requirements.
  • aa does not match a; aa matches banana.
  • Scan the name once.
Hint — after your attempt

Track the next code character to match.

Extension: Add a separate contiguous-substring mode and demonstrate the difference with tests.

Back to this stage

Clean a kitchen note

Implement clean_note(text): remove only the literal tokens :-) and :-(, then replace the first and last ! with a dot. A single ! is replaced once. Preserve all other characters and spaces. "Hot! :-) urgent!!" becomes "Hot. urgent!.". Implement another function that deduplicates a dish code while preserving first occurrence: "abacb" → "abc".

Check your result

  • Cover empty input, no ! and a single !.
  • Do not remove ordinary parentheses or colons.
  • Keep the two spaces in the example.
Hint — after your attempt

Separate token removal from locating the first and last positions.

Extension: Explain how whitespace normalization would change the contract; do not add it silently.

Back to this stage

Compress display signals

A kitchen display records string statuses. Compress adjacent equal statuses into (status, count) pairs: ["ready", "ready", "busy", "ready"] → [("ready", 2), ("busy", 1), ("ready", 1)]. Also implement decompression. Statuses may contain digits and delimiters; keep the representation unambiguous.

Check your result

  • Do not combine equal statuses separated by a different status.
  • Decompressing compressed input reproduces the original list.
  • Reject zero, negative and non-integer counts when decompressing.
Hint — after your attempt

Keep the current value and repetition count separately.

Extension: Make a streaming generator that does not retain the entire input.

Back to this stage

Reverse an allergen directory

Reverse {"dish-7": "nuts", "dish-2": "milk", "dish-9": "nuts"} into {"nuts": ["dish-7", "dish-9"], "milk": ["dish-2"]}. Preserve input order within each list. Next, process {"lunch": {"dish-7": 2}, "dinner": {"dish-7": 1}}: emit full-path records without losing identical keys from different branches.

Check your result

  • Colliding values are not overwritten.
  • The nested example produces different paths: (lunch, dish-7) and (dinner, dish-7).
  • Cover empty input and depth greater than two; define allowed leaf types.
Hint — after your attempt

The reverse mapping has multiple values per key.

Extension: Add code renaming with an explicit error when new keys collide.

Back to this stage

A menu item in a set

Create an immutable MenuItem(menu_id, code, title). Equality and hashing depend only on (menu_id, code); titles may differ. The same code in two menus denotes different items. Add clear str and repr; show an individual object and a list of objects.

Check your result

  • Equal objects have equal hashes and deduplicate in a set.
  • Fields cannot change after insertion into a set.
  • Handle comparison with an unrelated type correctly.
Hint — after your attempt

Define the domain identity of a menu item first.

Extension: Explain why a list display uses a different representation method than printing one object.

Back to this stage

Container packs for an order

A kitchen has one pack of 6 containers and two packs of 4. Implement a callable PackStock: a request selects whole packs totaling exactly the requested quantity and reduces stock. A request for 8 uses the two packs of 4; a request for 5 fails without changing stock. Minimize the number of packs; break ties by preferring larger packs. Sizes and requested quantities are positive integers; zero stock is valid.

Check your result

  • A greedy largest-pack choice must not lose the solution for 8.
  • Do not dispense more packs than are in stock.
  • A failed search leaves no partial mutation.
Hint — after your attempt

Separate combination search from applying the result to inventory.

Extension: Set input-size limits and explain when another algorithm is needed.

Back to this stage

A diamond in a handler hierarchy

Create Base, Audit(Base), Cache(Base) and Endpoint(Audit, Cache). Each handle() appends its class name to a shared trace; intermediate classes call super().handle(), while Base ends the chain. Predict the call order and verify it using Endpoint.__mro__. Fix a version where Audit calls Base directly and skips Cache. Demonstrate class versus instance attributes using an audit log list.

Check your result

  • Order: Endpoint, Audit, Cache, Base; Base is called once.
  • Swapping base classes changes the order predictably.
  • Instance logs do not accidentally mix.
Hint — after your attempt

In a super chain, the next class follows the concrete object’s MRO.

Extension: Compare this hierarchy with composing independent handlers.

Back to this stage

Register notification types

Given {"email": {"retry_limit": 2}, "screen": {"retry_limit": 0}}, create a handler type for each key using a regular class factory, then show an equivalent using type(). Instances must have separate message queues. Reject duplicate type registration explicitly. Discuss when dynamic classes are inferior to a handler dictionary, and why a Singleton in one Python process does not coordinate multiple processes.

Check your result

  • Names and retry_limit match configuration, including 0.
  • Queues are independent and duplicate registration fails.
  • Explain the design limits without claiming a global Singleton.
Hint — after your attempt

Do not store a mutable queue as an attribute of the generated class.

Extension: Validate the entire configuration before creating any type.

Back to this stage

Each callback sends its own status

Build three callbacks for queued, cooking and ready inside a loop. After the loop finishes, each callback must return its own status and the supplied order_id. Demonstrate the broken version where all return ready, explain when the variable is bound, and fix it in two ways. In the public function, make order_id positional and status keyword-only.

Check your result

  • Three callbacks produce three different statuses for one order_id.
  • The function signature rejects positional status.
  • Do not replace a sender exception with a generic success.
Hint — after your attempt

Compare closing over a variable with capturing its current value in a separate scope.

Extension: Write tests that call callbacks after registration completes.

Back to this stage

Rotate kitchen shifts

Implement cycle_staff(names, start=0), yielding names indefinitely in a cycle. Input is a finite collection, snapshotted when the iterator is created. For ["A", "B", "C"] and start=1, the first five values are B, C, A, B, C. Empty input terminates without yielding. Add a finite wrapper that takes at most n shifts.

Check your result

  • Mutating the input after iterator creation does not change the schedule.
  • Zero n gives an empty result; reject negative n.
  • Do not materialize an infinite list.
Hint — after your attempt

A generator function starts executing on the first next; account for snapshot timing.

Extension: Add a generator of cumulative shift counts per staff member.

Back to this stage

Stream receipt-line totals

Each file line contains dish_id;quantity;price_minor. Example: 7;2;125, 9;1;300, 7;1;125. Implement a record generator and compute the total: 675. Skip blank lines. An invalid line raises an error including its line number; quantity is a positive integer and price is a nonnegative integer. The file may exceed available memory.

Check your result

  • The example totals 675; zero-price lines are valid.
  • Error numbers refer to the original file, including blank lines.
  • Closing the iterator releases the file; do not load it all at once.
Hint — after your attempt

Separate parsing one line from owning the file resource.

Extension: Return per-dish totals and explain the resulting memory bound.

Back to this stage

Audit a function result

Implement audit(operation, sink) for a synchronous function. The sink receives exactly one record: operation, outcome (ok or error) and duration. Preserve function arguments and return values; propagate its exception. Do not include passwords or argument values in the record. Use a monotonic clock; the sink does not raise exceptions in this exercise.

Check your result

  • Success and failure each produce one record.
  • Preserve metadata with wraps.
  • Tests replace the clock and sink without sleeping.
Hint — after your attempt

Identify what must happen on both success and failure.

Extension: If the sink can fail, explicitly define which result the caller sees.

Back to this stage

Publish a report atomically

Implement the report_writer(path) context manager. Write a temporary file in the same directory. On successful exit, replace the previous report; on error, keep the old report, delete the temporary file and propagate the exception. State the guarantee for one process and normal completion; power-loss durability is a separate extension.

Check your result

  • The old report remains fully readable before exit.
  • A mid-write exception leaves the old file intact.
  • No open handles or temporary leftovers remain after success or failure.
Hint — after your attempt

The temporary file lifecycle and destination replacement are separate steps.

Extension: Analyze two concurrent writers and define the extra guarantee required.

Back to this stage

Collect supplier prices asynchronously

Implement collect_quotes(suppliers, fetch, limit, timeout): at most limit fetch calls run concurrently. Preserve supplier order and represent each call’s success or error separately. Start each positive per-supplier timeout after acquiring an execution slot; cancellation of the entire operation must cancel its tasks and propagate. Limit is a positive integer. Add an async duration decorator and demonstrate why synchronous sleep blocks the event loop.

Check your result

  • A test fetch records peak active calls; it never exceeds limit.
  • One failure does not discard successes; cancellation is not disguised as an ordinary error.
  • No tasks remain afterwards; tests control clocks and delays.
Hint — after your attempt

Separate a supplier failure from cancellation of the whole operation.

Extension: For CPU-heavy work, compare the event loop, a thread and a process using measurements.

Back to this stage

A ticket checksum

A ticket number is a nonnegative integer without leading zeros. Select numbers whose decimal digit sum is divisible by 7. For [0, 7, 16, 25, 34, 99], return [0, 7, 16, 25, 34]; preserve order and duplicates. Reject strings, booleans and negative numbers. Show integer-division/remainder and string-conversion approaches, then compare them.

Check your result

  • Handle zero explicitly; it qualifies.
  • Include every digit of a multi-digit number.
  • Explain complexity by digit count, not just ticket count.
Hint — after your attempt

The remainder modulo 10 gives the current last digit.

Extension: Yield qualifying tickets in an inclusive range without materializing the entire range.

Back to this stage

Count tasting-menu combinations

Choose k of n distinct dishes without replacement; serving order does not matter. Implement menu_combinations(n, k) with an exact integer result. (5, 2) gives 10 and (0, 0) gives 1. Allow 0 ≤ k ≤ n ≤ 200; reject booleans and non-integers. Compare factorial-based calculation, a reduced product and a table for repeated queries. Do not use floating-point intermediate values.

Check your result

  • Test k=0, k=n and symmetry between k and n−k.
  • Invalid ranges raise explicit errors.
  • For repeated queries, measure redundant work and cache memory.
Hint — after your attempt

Identify factors that cancel before computing large factorials.

Extension: Generate all values for one n and verify their sum: 2**n.

Back to this stage