Today, I hid from the heat, and did some more work porting Lox interpreter stuff to Python. I decided to move on in the book, to the actual interpreter logic, and getting it ported to Python is a little tricky. The problem isn't strictly expressing the logic of the Lua or Java version, it's expressing the logic in a way that's visible to static analysis.
Fortunately, just complaining about the problem in general terms turned out to provide all the insight I needed to deal with the problem. However, I do see that there's trouble on the horizon I'll need to deal with later: The Lua version manages to define the expression visitor so that it passes through additional arguments. I'm not sure how to express that with function signatures in Python. When I was first thinking about this, I kind of punted it like "Oh, well, it an act like it's just operating on the first argument, but actually it's allowed to return a callable, so the signature can be something like ExprVisitor[Callable[[Environment], Tuple[Environment, LoxObject]]]" and maybe these callables are, like partial applications of private methods? I don't know, I shouldn't rush into implementing this stuff, so I'm going to sleep on it, and also flee this terrible warmth.