You've done a great job demoing a variety of patterns and tools. I'll add two more things you could benefit from learning early:
- Type hints. Function headers are almost always clearer with type hints, and you need them if you want to use a tool like MyPy.
- The main method pattern.
- (Usually I'd also advocate for recursion when someone's demo project is a REPL game like this, but in your case I don't think it would improve this code.)
Other stuff:
- Usually it's better to avoid exceptions when you have other options. While you could do what you're doing here by other means, you've done a few things that make me like your
check
function: It's concise, it bundles away a discrete bit of functionality, and you're catching a narrowly defined exception class. - Unfortunately, it's a little clunky to have a function that just tells you if
int(x)
threw an exception, just so you can callint(x)
on the next line. You have many other options; I'd be fine withdef check (user_input: str) -> Optional[int]:...
, but then you must useif x is [not] None:...
later. - Depending exactly what the function in question does, either
validate
orsanitize
would probably be better thancheck
. - I lied: another new thing to learn: itertools. In particular, a
while
loop that increments something is always begging to get replaced with afor
loop. In this case, since we want to keep going "as long as it takes", we need a lazy infinite iteratable:itertools.count(0)
.- In order to make that work, we'll need to separate out the "that input was invalid, try again" logic into a separate loop (or recursive function).
- And then if you teach yourself generators you could write
for (try_number, user_input) in zip(itertools.count(0), yielded_user_input()):...
. Fun times!