And there is nothing wrong with using a type checker for your Python type annotations.
And there is nothing wrong with using a type checker for your Python type annotations.
"And there is nothing wrong with using a type checker for your Python type annotations."
There is a lot wrong with that. Python is a dynamically typed language. If you are using a static type checker on a dynamically typed language, you are not taking advantage of the dynamic nature of the language.
You are basically not writing Python code at all. This means you get all the disadvantages of using a dynamically typed language with none of the benefits.
In summary, you have a made a very poor engineering decision.
Typing related bugs that aren't caught in unit testing are very rare.
I disliked JavaScript and love python. Typescript has replaced python as my labor of love language for now but a typed python in a similar manner would be great
JavaScript is a bad language and adding stuff to it makes it better. The same isn't true for Python.
This is a little misleading as mypy and pyright both support structural typing.
You can still do duck typing. Python's Protocol and TypedDict do that for objects and dicts, respectively. Python also has sum types. I've added type annotations to thousands of lines of legacy code and rarely have I had to change any logic to accommodate.
> Typing related bugs that aren't caught in unit testing are very rare.
Not true at all. Unless you're writing unit tests that cover every possible code path, your unit tests will miss stuff. And I'd argue that 100% test coverage is a Herculean effort to write and maintain, and isn't worth it. Static analysis gives you tons of checking for free. The biggest benefit is forcing you to properly handle function arguments that could be None. Are you always checking that values aren't None before using them?
It searches for a very narrow and rare set of bugs, which hardly ever happen.
Typing really exists to help a compiler, it's not a code correctness tool in the way you envision.
I really dislike this take. I believe Python is about rapid iteration, doing more of functionality with less code, and enjoying the process of programming. You know what I enjoy? Easily knowing what features (methods, attributes, keys) an object has. You know what I really don't enjoy? Having to dig through reams of source code to figure out what attributes I can use, or what *kwargs a function takes.
Nothing about duck typing prohibits saying `def func(duck: Duck)`, because maybe it's a French duck and has .coin() instead of .quack().
Also also, how is static typing completely incompatible with dynamic features? Usually there exists an abstraction with static types that actually captures the behavior you want. Its rare you want to be able to handle literally anything. Ask oneself, "could this function receive absolutely, truly, anything, or is it just shortcut for not finding the right types?"
"Having to dig through reams of source code to figure out what attributes I can use, or what *kwargs a function takes."
Figuring out the attributes and what kwargs a function takes is the role of your intelligent IDE and docstrings. You think you need to static typing for this since you haven't spent the time to learn how Python does these things.
Duck typing says, duck doesn't have a type, not even Duck, anything that quacks is a duck even a 115 ton truck.
I've been writing python for over 15 years and use Pycharm extensively. Please don't make assumptions about other users' experiences without evidence.
IDEs absolutely do not infer what fields *kwargs take, let alone what types those fields are. I'm not talking named fields, I mean literally
def func(*args, **kwargs): ...
Afaict, no IDE and no type system can help you there, in python, save for some really arcane type inference magic on every single call site.> duck doesn't have a type, not even Duck
It absolutely does, if Duck is a protocol. If you have some object, and you call .quack(), it implements the Quacker interface, even without inheriting BaseDuck. You can still statically type it no problem, and it'll take a Truck no problem, so long as Truck.quack() is defined. Almost everything has a type, it just takes some effort to uncover it.
Ahh so if a method on an object dynamically adds in the quack() method at runtime, does the object spontaneously change type?
Yes, actually.
This is an antipattern in the vast majority of use cases. There are far better patterns out there than just sticking a method on an object, and I detest codebases that do this sort of thing. It's almost always someone who uses the phrase "pythonic" unironically way too much, writing these sorts of things, and a nightmare to debug.
There's less problem with doing something like
def quackerizer(foo: Any) -> Quacker:
foo.quack = lambda...
return foo
Still kinda odd, but at least you are doing a formal cast.> That's an IDE problem, with sufficently powerful code inspection, the fields can still be found.
Ok, name one. Pycharm and vscode don't currently do it afaik. Maybe some vscode plugin, haven't used vscode much.