On the contrary, the dynamic nature of python can make it seem like inaccessible black-magic where you don't even know where to look in the code to see what's even possible. Stronger types will empower users to understand their ecosystems.
I've spent countless hours in rails/django land trying to figure out what's even possible with an object that the ORM or whatever gives me. The human-flavored docs kinda outline it, but if you're in IDE-land, context-switching to prose isn't productive.
Types help humans develop and debug, but they make library authors jump through acrobatics to express what their magic does. This is a limitation of the type-checker implementation(s) not having a strong syntax or notion to capture the runtime-dynamic polymorphism, but it doesn't mean that the concept of types is a flawed idea or not worth using when appropriate.
Don't throw the baby out with the bath-water.
Good point about Django though. Django has so much historical stickiness that types probably look attractive for entrenched corporate projects that are hard to port to high performance natively typed languages.
The Python community and the set of people that treat mypy and pylance as dictators rather than tools to be used where appropriate and turned off where not are...not the same thing. (And the latter very much depends on tools built by the rest of the former that internally are wild and woolly, even if they present a nice cleanly typed interface to the consumer.)
Sure, and that is a problem with exposing certain kinds of dynamism when your customer base is the kind that is inflexible about typing.
OTOH, a lot can still be done with dynamism on the inside and a typed public interface, and also there's a significant part of the community that accepts selective disabling of typechecking as an acceptable when dynamism provides adequate benefits.
class Inject:
def __init_subclass__(cls):
cls.injected = 42
class Child(Inject):
injected: int
print(Child().injected) # 42
This technique isn't too useful in practice, though it has its place (e.g. when creating ctypes.Structures). But there's a clever way to simplify this. Remember that the subclass is a subclass -- it inherits from Inject: import typing as t
class Inject:
injected: t.ClassVar[int]
def __init_subclass__(cls):
cls.injected = 42
class Child(Inject):
pass
print(Child().injected) # 42
(ClassVar[int] ensures that type checkers know it's a variable attached to the class, not each instance.)Also see one recent HN discussion on "An Oral History of Bank Python" [2].
The truth is, the argument that you cannot implement complex systems in dynamic languages has always been empirically fraught because there are so many counter examples. The problem is, even if static languages are "better" for such things, dynamic languages are certainly not bad enough at it to prevent them being successfully used for large scale systems.
Disagree. It's popular because it has an uncluttered syntax - "executable pseudocode" - and simple semantics - "explicit is better than implicit", "there should be one obvious way to do it", and all that. Metaclasses were never particularly Pythonic (if anything they felt like a more Ruby-style way of doing things, and the ascent of Python over Ruby is a reflection of that difference).