| ▲ | pansa2 7 months ago |
| The biggest issue with Python type hints for me isn't the hints themselves, it's that they encourage people to write overly complex, verbose code just to satisfy the type checker. Code like this [0] could simply be 3 functions. Instead it's 3 classes, plus a base class `AstNode`, just so the author can appease the type checker by writing `body: List[AstNode]` instead of the dynamically-typed `body = []`. [0] https://gist.github.com/sportsracer/16a1e294966cfba83ba61e6a... |
|
| ▲ | maleldil 7 months ago | parent | next [-] |
| Writing `body: list[AstNode]` lets you statically know what elements you'll get when you do `body[i]` or iterate over it. If you don't specify the type, you don't know what you're getting, and you have to rely on always passing the correct objects around. I'm sure you've faced bugs where you expected something from a list and got something else. Note that you only need `body: list[AstNode] = []` if you declare an empty container since the type checker cannot infer the type. If you're using list comprehensions or initialising the list with an element, the type checker will infer the type. You can even have a heterogeneous list with objects from multiple types, and the type checker will infer the type as the union. In modern typed Python, you usually only annotate function signatures or stuff like dataclasses. |
| |
| ▲ | pansa2 7 months ago | parent [-] | | > Writing `body: list[AstNode]` lets you statically know what elements you'll get when you do `body[i]` Don't get me wrong, I understand the benefit of the type hint - and if typing `: list[AstNode]` was all it took to get that benefit it would be a no-brainer. But that's not all it took - instead, to pass the type checker the entire script has become twice as long as it needs to be. The actual logic is hidden among multiple class definitions which are all unnecessary except to satisfy the type checker. I agree with Armin Ronacher [0]: "types add value and they add cost". Perhaps the cost is worth paying for most programs - but there are plenty of other languages that support that way of working. For those programs where the cost might outweigh the benefits, I used to love writing Python as a more concise alternative to those languages - but instead nowadays it has become a poor attempt at mimicking them. [0] https://lucumr.pocoo.org/2023/12/1/the-python-that-was/ | | |
| ▲ | maleldil 7 months ago | parent [-] | | > there are plenty of other languages that support that way of working I'd use them if I could, but I'm trapped in Python for several reasons. If I could use Rust, Ocaml, etc., I'd do that. But my choice is between untyped and typed Python only, and I strongly believe that typed Python is much better. |
|
|
|
| ▲ | dikei 7 months ago | parent | prev [-] |
| That code looks like a proper object-oriented design to me, nothing to do with type-hints actually. |
| |
| ▲ | pansa2 7 months ago | parent [-] | | Type hints encourage this sort of object-oriented design though, in my experience. The resulting code is extremely verbose compared to Pythonic "executable pseudocode". For example, see Jack Diederich's talk "Stop Writing Classes": https://www.youtube.com/watch?v=o9pEzgHorH0 | | |
| ▲ | maxbond 7 months ago | parent [-] | | That talk had a big impact on my coding style. But citing a 99 line script written as an example for a blog post doesn't really support your argument. 99 lines is short, and verbosity is expected in such example code. Consider FastAPI. It uses functions as endpoints, like flask. Very compatible with "Stop Writing Classes." It also leverages type hinting to eliminate boilerplate and create more concise code. You don't have to put validation or dependency injection logic at the top of every endpoint, it's handled for you so you can dedicate screen space to the problems you're solving. Consider also the pythonism, "explicit is better than implicit." If memory serves, "Stop Writing Classes" wasn't so much about not writing containers for data but not writing containers for behavior when it wasn't associated with data. Behavior can live as a freestanding function just as well as inside of an object. But it's difficult to understand the semantics of freestanding nontrivial data, like dictionaries or long tuples. Dataclasses and pydantic models require a minimum of boilerplate and couple the data with it's semantic meaning, so that it's preserved across boundaries. I for one am never going back to the Python before these tools. | | |
| ▲ | kstrauser 7 months ago | parent [-] | | Seconded, to all of that. Jack’s talk made a huge impression on me, too. So now I write almost no classes other than data containers, or maybe ones where I want to change some behavior without having a gazallion “isinstance” calls. All the happy little functions are thoroughly type decorated. |
|
|
|