- Imports everywhere. Even basic types (list, dict, set) have to be imported
- Basic types don't share the same casing as the actual types they represent (Set vs set, Dict vs dict, List vs list)
- Awful support for enums.
- Syntax is generally ugly. Typescript got everything right; it's intuitive, clear, and scales pretty well even for complex types. I'd be happy if they just copied typescript syntax.
- Types have a runtime impact.
Do you mind expanding on this, and what could be done differently?
x: float = 1.0
instantiate?Complicated closures are often more idiomatically as classes where the aggregate type would be a class member.
If you are just calling the enclosed function then of course this does not happen. Compare:
def mytype():
print("!")
return dict()
def closure():
def enclosed(x: mytype()):
pass
return enclosed
print("Calling the closure")
for i in range(10):
closure()
print("Calling the enclosed")
f = closure()
for i in range(10):
f(0)
Note that using closures is almost always faster in Python than classes, because accesses to enclosed variables are done through offsets instead of attribute lookup (this gap has been somewhat closed, but is still significant in some cases; arguably almost all of these cases indicate that you are using either the wrong language, or the wrong implementation). So adding overhead to closures can be a somewhat valid reason to avoid using these annotations.Reading and writing types should be fast, agile, easy and effortless. Only way to acheive that is having it as close the usage as possible
Althought now annotations don't have a runtime cost anymore since the y are lazily evaluated.
from typing import TypeVar
T = TypeVar('T')
in every module where you want to parametrize a type.
(not sure how that works with 3.6+'s unevaluated annotations. that's what my linter thinks)also callable types are really ugly... this:
Callable[[Foo, Bar], Baz]
vs something like `(Foo, Bar) -> Baz` is just yuckputting constraints on a type is pretty ugly too iirc, i think it looks something like
F = TypeVar(bound=Foo)
def bar(a: F, b: F) -> F: ...
hacking type annotations into the language's existing syntax is hard :(If you have a function that takes `(Foo, Bar) -> Baz`, What you really have is a function that takes in a FoobarBazifier.
So name your function-interface:
FoobarBazifier = Callable[[Foo, Bar], Baz]
def my_func(cb: FoobarBazifier) -> Baz:
...
This is pythonic, in the same way that preferring named functions over lambdas (and therefore not having a single character lambda syntax) is pythonic. Its controversial and people dislike it, but it is nice.It also has the side effect of letting you Subclass the interface if you want to (via NewType), so that if you have multiple functions that match the same signature but fulfill different interfaces, you can name the interfaces differently and use them in different places.
Java mostly enforces this because you have to define an explicit interface and class for your callbacks (or you did until java 8), this is essentially the same thing, but with 1 line of boilerplate instead of 25 across 3 files.