We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Pattern Matching, f-Strings and Newer Syntax step 16 of 23
Make Arguments Positional-Only or Keyword-Only
What does this call do?
clamp(2.0, 0.0, 1.0)
You have to open clamp to learn which number is the value and which are
the bounds. A signature can settle that for every caller:
def clamp(value: float, /, *, low: float = 0.0, high: float = 1.0) -> float:
return max(low, min(high, value))
clamp(2.0, high=1.0) # 1.0
clamp(2.0, 0.0, 1.0) # TypeError, low and high must be named
clamp(value=2.0) # TypeError, value cannot be named
- Parameters before
/are positional-only. Callers pass them by position, never by name. - Parameters after a bare
*are keyword-only. Callers must name them, as inhigh=1.0. - Parameters between the two markers accept either form.
Keyword-only parameters make each call say which value is which. Positional-only parameters let you rename a parameter later without breaking any caller, because no caller can use its name.
Leave a name free for **attrs
A registry takes any attributes as keyword arguments. With
def register(self, name, **attrs), the call
registry.register("widget", name="label") fails with
TypeError: ... got multiple values for argument 'name'. Put a / after
name, and name="label" goes into attrs instead. self comes before the
/ too, so an attribute called self also works.
Let each class name its own parameter
The starter defines a Serialiser protocol. A Protocol lists the methods
a class must have. Any class with matching methods fits it, without
inheriting from it.
class Serialiser(Protocol):
def dumps(self, payload: str, /) -> str: ...
The / means no caller can write dumps(payload=...), and mypy rejects any
call that tries. So Upper can call the parameter text, Reversed can call
it chunk, and every call still works. Without the /, a caller could write
payload=, and that call would fail at runtime on Upper, with no warning
from mypy.
What to write
Upper.dumps(text)returnstext.upper().Reversed.dumps(chunk)returnschunk[::-1].- In
solve, put anUpperand aReversedin alist[Serialiser], so mypy checks that both classes fit the protocol. clamp(value, /, *, low=0.0, high=1.0)returnsmax(low, min(high, value)). Whenlowis greater thanhigh, that returnslow. Keep it that way, and don't swap the bounds or raise.Registry.register(self, name, /, **attrs: str)stores a copy ofattrsundername, replacing any earlier entry.snapshot()returns a new dictionary with a copy of each entry, so changing a snapshot never changes the registry.call_outcome(fn, *args, **kwargs)callsfnonce. It returns"TypeError"if the call raisesTypeError, and"ok"otherwise. Let any other exception reach the caller.
Build the report in solve
Make a new Registry and register name with attrs. Return:
| Key | Value |
|---|---|
clamped |
clamp(value, low=low, high=high) |
positional |
call_outcome(clamp, value, low, high) |
keyword |
call_outcome(clamp, value=value) |
serialised |
Two lists, the Upper results for every payload and then the Reversed results |
registry |
The registry snapshot |
Both call_outcome calls must return "TypeError". For payloads
["ab", "c"], serialised is [["AB", "C"], ["ba", "c"]], and no payloads
give [[], []]. An empty name and an empty attrs are valid.
Why the wrong calls go through call_outcome
Your submission must pass mypy --strict, and mypy rejects a direct
clamp(value, low, high) with "Too many positional arguments". call_outcome
takes Callable[..., object], and the ... tells mypy to accept any
arguments. So the wrong call passes the type check, and Python raises the
TypeError when it runs.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.