Remix.run Logo
brap 4 days ago

I’m far from being an experienced Pythonista, but one thing that really bugs me in Python (and other dynamic languages) is that when I accept an input of some type, like User, I have to wonder if it’s really a User. This is annoying throughout the codebase, not just the API layer. Especially when there are multiple contributors.

The argument against using API models internally is something I agree with but it’s a separate question.

3eb7988a1663 4 days ago | parent | next [-]

Do you mean like is the User object is a well formed User, or did someone actually give you an int?

As to the first problem, I recommend the Parse don't validate post[0]. The essential idea is stop using god objects that do it all, but use specific types to make contracts on what is known. Separate out concerns so there is an UnvalidatedUser (not serialized and lacking a primary key) and a ValidatedUser (committed to the database, has unique username, etc). Basic type hinting should get you the rest of the way to cleaning up code paths where you get some type certainty.

[0] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

padjo 4 days ago | parent | prev | next [-]

Python has reasonably good types these days. If you were to use pydantic to Marshall stuff from the API and then put type annotations on every method below that it would be pretty bulletproof.

ac130kz 4 days ago | parent | prev | next [-]

Somewhat solved by type annotations + a good static type checker, such as pyright (it's 2025, there must be type annotations everywhere), and dynamic cases (very rare, probably due to poor or unfortunate design decisions) can be solved with validators, e.g. the aforementioned Pydantic. This isn't a silver bullet, but it works really well.

codethief 3 days ago | parent [-]

Yeah, exactly. There isn't a single good reason not to use type annnotations in Python these days. Yes, they might not be as powerful as TypeScript's type language yet, but they are getting there.

derriz 4 days ago | parent | prev | next [-]

I've been using Python on and off for a few decades and agree. I don't know why you're being downvoted.

I've authored tens of thousands of lines of Python code in that time - both for research tools and for "production".

I use type hints everywhere in the Python I write but it's simply not enough.

This issue is political and not so much technical as Typescript demonstrates how you can add a beautifully orthogonal and comprehensive type system to a dynamic language, thus improving the language's ergonomics and scaleability.

The political aspect is the fact that early Python promoters decided that sanity checking arguments was not "pythonic" and this dogma/ideology has persisted to this day. The only philosophical basis for this position was that that Python offered no support for simple type checking. And apparently if you didn't/don't "appreciate" this philosophy, it reflected poorly on your software engineering abilities or skill with Python.

To be fair, Python isn't the only language of that era, where promoters went to great lengths to invent alternate-reality bubbles to avoid facing the fact that their pet language had some deep flaws - and actually Perl and C++ circles were even worse and more inward facing.

So the "pythonic" approach suggests having functions just accepting anything, whether it makes sense or not, and allowing your code to blow up somewhere deep in some library somewhere - that you probably didn't even know you're using.

So instead of an error like "illegal create_user(name: str) call: name should be a str but was a float", it's apparently better (more "pythonic") to not provide such feed-back to users of your functions and instead allow them to have to deal with an exception in a 40 line stack trace with something like "illegal indexing of float by dict object" in some source file library your users haven't even heard of.

akkad33 4 days ago | parent | next [-]

> This issue is political and not so much technical as Typescript demonstrates how you can add a beautifully orthogonal and comprehensive type system to a dynamic language, thus improving the language's ergonomics and scaleability.

How does typescript demonstrate this?

I don't see how typescript is different from Python in this regard. Typescript compiles down to JavaScript, which like Python is dynamic. So at runtime nothing prevents you from calling a function written to take ints with strings. In fact, JavaScript has even worse typing than Python, so I imagine it's worse.

derriz 3 days ago | parent [-]

Typescript demonstrates that you can have a fully dynamic language but also provide a type system which can support as much (or as little) type checking as is appropriate or desired.

I can take my chances in Typescript by just using 'any' everywhere but if I do want to constrain variables to particular types, the compiler will fully support me and provide guarantees about the restrictions I've specified via the type signatures.

akkad33 3 days ago | parent [-]

It sounds exactly the same as Python with pyright or mypy. A novel approach is taken by Elixir, which actually makes sure the compile time types match runtime types. That is , you can't call a function with an incorrect input type at runtime https://elixir-lang.org/blog/2023/09/20/strong-arrows-gradua...

dontlaugh 4 days ago | parent | prev | next [-]

I’ve also used Python for well over a decade and nowadays I mostly don’t. But for that reason and because of the terrible performance / efficiency.

zo1 4 days ago | parent | prev [-]

The problem with that small tidbit is that it immediately sets your type system to go down the path of Java and Typescript (which we all mock for it's crazy type systems and examples such as IImplementsFactoryAbstractMethodThingVirtual classes). This is not the python way, and is frankly part of its secret sauce (if you ask me).

And yes I include Typescript with Java there because it has it's own version of the Java class ecosystem hell, we just don't notice it yet. Look at any typescript library that's reasonably complicated and try to deduce what some of those input types actually do or mean - be honest. Heck a few weeks back someone posted how they solved a complicated combinatorial problem using Typescripts type system alone.

derriz 3 days ago | parent [-]

I don't get your point or what it has to do with the "pythonic" suggestion that you don't check early for incorrect state/arguments?

Any language, including Python, which supports the concept of a class will allow you to write a class called IImplementsFactoryAbstractMethodThingVirtual. And none of Java, C++, Python, Typescript, CLISP, etc. prevent you from building or designing an overly complex class model.

It has nothing to do with ensuring a particular argument to a function or method is of the expected type, which was my point - the "pythonic" way is to NOT check.

I also do not understand your example of Typescript. Compared to the last time I worked on a Javascript code-base, recently having to work with a Typescript code-base was a joy including reading library code. Stripping out the types gives you Javascript - surely you are not claiming that it makes it easier to read libraries with the type signatures removed?

Whether a library is complex or not is completely orthogonal. Doing it regularly, navigating the source of an overly-complex Python library is no fun either.

jon-wood 4 days ago | parent | prev [-]

I’m curious, what do you mean by having to wonder if it’s really a User? It’s optional in Python but you can use type annotations and then the type checker will shout at you for passing something that’s not a User instance to things that expect one.

brap 4 days ago | parent [-]

The type checker can be ignored in all sorts of ways

akkad33 3 days ago | parent [-]

I mean, that is implicit in the statement. The question is how