Hacker News new | past | comments | ask | show | jobs | submit login
Type hinting for Python (lwn.net)
122 points by zedpm on Jan 6, 2015 | hide | past | favorite | 70 comments



Apologies to everyone tired of hearing about it, but an alternative non-standard syntax is my own obiwan https://pypi.python.org/pypi/obiwan


There's also https://github.com/prechelt/typecheck-decorator

...which I prefer stylistically (decorators instead of magic monkey-patching).


My contribution is https://github.com/kislyuk/ensure

It's fast and doesn't monkey-patch things.


    @ensure_annotations
    def f(x: int, y: float) -> float:
        return x+y
This looks great, I wish it was the standard


Cool, I like this. I think I prefer your reuse of the collection literal syntax to the syntax in Guido's proposal.


There's also https://github.com/prechelt/typecheck-decorator

...which I prefer stylistically (decorators instead of magic monkey-patching).


There's also https://github.com/prechelt/typecheck-decorator

...which I prefer stylistically (decorators instead of magic monkey-patching).


There's also https://github.com/prechelt/typecheck-decorator

...which I prefer stylistically (decorators instead of magic monkey-patching).


HN comments from a previous submission of Guido's pre-PEP, "The Theory of Type Hinting": https://news.ycombinator.com/item?id=8775638


Having used optional type annotations in Dart for many many months now I really miss them whenever I'm diving through our backend Python code base.

I don't use them heavily but they are a great way to sprinkle some clarity over function signatures and APIs you care about and want to 'fortify'.

It's interesting to see the concept gaining traction in the Python and PHP communities.


Off-topic: Curious.. what are you making in Dart?

Personally I am in love with the language but worried about it not gaining enough traction and ending up abandoned because of it.. that's keeping me from using it for non-trivial side-projects.


This is great!

PHP is trying to provide full support for hinting (you couldn't hint a scalar type nor returns)

I can see how most languages are slowly converging.


Gilad Bracha's been pushing for languages with optional type systems for some time:

https://www.google.com/webhp?q=bracha+optional+type#safe=off...


The proposal for square brackets List[int] in generics is the weirdest thing. There is a standard un-surprising way to do this and it is angle brackets List<int>. What is the reason for forcing developers to use this jarring syntax?


Angle brackets cause difficulties during parsing: since there is a '>>' operator, getting a type like 'List<List<int>>' to parse properly would require either a hack in the lexer or requiring a space between closing angle brackets (as in 'List<List<int> >')

This is a well-known problem in C++; some of the parsing rules were changed in C++11: http://stackoverflow.com/questions/15785496/c-templates-angl...


Angle-brackets may be familiar to C++, Java, C# and friends. But square-brackets are used by Scala (and maybe others).


Eiffel also uses square brackets.


I can think of one reason: `<` and `>` are comparison operators in Python, and they are easily overloaded with the `__gt__` and `__lt__` magic methods. So, if `List` was an instance of an object with those overridden magic methods, it's unclear what you actually want to do (i.e., `List<int` might evaluate to True, and then `True>` would lead to a syntax error). `[` and `]` have no such limitations.


"`List<int` might evaluate to True, and then `True>` would lead to a syntax error"

You are confusing parsing and reduction. In most languages - and almost certainly in Python - these are separate steps. "Parsing it wrong leads to a syntax error" is a good thing - it means you're forced to parse it right. It would be worse if there were multiple syntactically-valid interpretations (which there may well be).


Here's the problem: `a<b>c` is already valid python. `3<5>2` evaluates to true, because python allows operator overloading. Because the < and > operators can be overloaded, there is no guarantee that an object of type "class" will not have them (this would, I believe, require some metaclass hackery, but still).

So, given that `object<int>()` throws a type error in python and not a syntax error, you can't unambiguously parse that.


Yeah, that's for sure a problem. There are potential ways around it, but none of them great (that I see, at least).

"So, given that `object<int>()` throws a type error in python and not a syntax error, you can't unambiguously parse that."

Apparently in Python 2, it's not even an error.


I think < > are pretty jarring. Especially reading 3 nested levels of it. [],() or {} I find are easier on the eyes in general.


Note that Guido's proposal doesn't add any syntax changes. The idea is not to modify the language but rather to have a unified format for defining type annotations. Function annotations have been there for many years now, and the only thing that will be added for now is some support in the standard library for the objects that will be used as type annotations.


You assume this is standard, but it's merely a crib from other languages. Haskell, for a zany instance, just uses spaces: `List Int`.


So does ML (albeit postfix), which came before Haskell or C++, right?


Definitely Haskell, probably C++


This was a bikeshed in the Rust community as well a pretty long time ago. They went with the `List<int>` style, which makes sense in light of C++ programmers being a large target audience of theirs, but that's far less a consideration in Python's case. I personally slightly prefer the square brackets, because it seems to imply "contains" because of its association with retrieving values from containers.


Calling that 'generics' is even weirder. What's so generic about a list of ints? That sounds pretty specific to me...


Angle brackets suck.


There is pretty much no reason to use <> except "that's what C++ did, because they didn't have any other symbols to spare".

If done correctly, (e. g. [] for types, () for values, and >/< for binary comparisons) this approach achieves a lot of consistency and simplicity.


There is pretty much no reason to use <> except "that's what C++ did, because they didn't have any other symbols to spare".

If done correctly, (e. g. [] for types, () for values, and >/< for binary comparisons) this approach achieves a lot of consistency and simplicity.


There is pretty much no reason to use <> except "that's what C++ did, because they didn't have any other symbols to spare".

If done correctly, (e. g. [] for types, () for values, and >/< for binary comparisons) this approach achieves a lot of consistency and simplicity.


I'm curious how this is valid Python:

  def fib(n: int) -> Iterator[int]:


  - Colon inside argument list
  - Arrow and type
What are they used for normally?


They're generic Function Annotations: https://www.python.org/dev/peps/pep-3107/


Has anyone explored the potential for these hints to be a front-end to Cython, or provide optimizations in CPython/PyPy? Static typing has big perf benefits too right, is that included in the eventual goals?


At least for Python, there are a different set of goals for programmer-productivity type hints (such as these) and for static-compilation type hints (such as you would need to provide optimizations).

One particular point is Python subclasses: it makes a lot of sense to say that a Python subclass is a subtype of the parent class, but in reality there are no guarantees about any relationship. This means that a compiler/VM would most likely have a hard time making use of type annotations since it would have to have to support the user passing arbitrary subclasses with completely different behavior.


Can we get multiple dispatch too?

Or is

- MD considered an anti-pattern these days?

- This no longer 'hinting'


I don't think anyone considers multidispatch to be an anti-pattern. Here's Guido himself emulating multimethods in Python via decorators: http://www.artima.com/weblogs/viewpost.jsp?thread=101605

They're certainly a bit obscure though. Julia is the only new language that I'm aware of that prominently features multiple dispatch as a first-class language feature: http://julia.readthedocs.org/en/latest/manual/methods/



This is a horrible idea.

of any language, python could be a poster-child for duck typing done right.

python is also elegant and very easy to read.

this is pythonic -

    def foo(a, b, c):
        pass
this is ugly/trash -

    def foo(a, b: int, c: str) -> str:
        pass

if this goes down and eventually leads to static typing i will have even less reason to consider using python 3.


I think I could get over that, but the idea of putting this hinting in comments is troubling for me:

    x = {}    # type: Dict[str, str]
I mean, it's very human-readable so it makes sense as a comment. It's optional, so not every declaration will have it. Still, something about the interpreter reading my comments is off-putting.


Yeah, that gives me the heebie jeebies. Fair enough if you want to use magic comments for an entirely third-party system like MyPy, but using them for a built-in language feature feels wrong.

Besides, how necessary is it? Presumably those constrained types can be instantiated:

    x = Dict[str, str]()
The constrained type's __new__ would be annotated appropriately, and return an ordinary dict. It's a bit less "pure" in that this is no longer strictly an annotation, but I'd much prefer that bit of impurity than magic comments.


Agreed. It seems like a simple typo e.g.

    x = {}  # tyep: Dict[str, str]
is a recipe for spending 6 hours wondering why you've got integers in your string-only dict.

(Assuming the interpreter ignores anything not matching `# type:`


Truly terrifying: I had to read your comment four times before I could find the typo.


Yeah, something like

    x: dict<str, str> = {}
would be nicer, I think.


Some advantages of type hinting:

- Editors and IDEs can use them to provide better autocompletion for you. That way you can discover how to use APIs while you write code without having to look at the docs all the time.

- Static analysers can tell you when you're making trivial errors just after you type them.

- They provide a form self documentation. In practice, most medium to large Python projects already add type signature information in the form of docstrings, using thing like '@param'. This is better because it's more uniform.

This is not against duck typing, you can use Abstract Base Classes to define an interface for some behaviour and match objects that provide that behaviour even if they don't belong to any specified type hierarchy.

Finally, this is completely optional. You can keep coding without types for your small scripts or private functions, while using type information for the APIs of big libraries.


- The interpreter/VM can use them to optimize some code to very fast equivalents or some data structures to very compact equivalents.


sounds like Cython. Are any such optimizations planned for 3.5 (or beyond)?


Not that I'm aware of. This type hinting is currently planned for analysis, not for efficiency.


On the other hand, this could be used for some nice "duck type checking".

Rather than the classic interface pattern, where the passed object must be a subclass of a certain type, these annotations could be used inversely to that. Something like:

   duckclass foo:
       def x(): pass
       def y(a: int) -> str: pass
       n: int

   def bar(it: foo) -> boolean: 
       ...
Where other classes don't need to explicitly inherit from foo, but rather, a checker can raise a warning where you are passing something that doesn't have an integer attribute (or getter function) called n, and the x and y functions. This allows some nice analysis, while still allowing good duck typing.

There are ways to do this without special keyworks too... that was just a quick first example. (maybe something like:)

   class foo:
      def x(): pass
      def y(a: int) -> str: pass
      n: int

   def bar(it: duck_as(foo)) ->str: pass
and so on.


You can have this today by inserting an explicit decorator that validates annotated arguments. You can even skip the decorators by dynamically wrapping callables with the validator.


What's so ugly/trash about that?

It looks quite simple/elegant. Plus what they're added is meant to be used by static analysis tools and IDEs, not actually for validating things at runtime.


What's the point if you don't actually validate with the validation hints you've added?


It's mostly a performance related choice.

Having these checks available for development does allow you full confidence in the type safety if the entire call stack has type annotations. If you're using some code that doesn't have these annotations, you might catch problems during development, or might not. But that's just the old familiar situation, and it allows you to iterate quickly while choosing which parts are more critical.

Note that this is analogous to the design choice made by Typescript, while the designers of Atscript chose to extend Typescript with runtime assertions as well.


Nonstatic typing brings down productivity very noticeably whenever I use it on anything beyond a small script. It introduces a large category of errors to make that static typing prevents; static typing makes the program more cohesive, structured, rigorous, and predictable. The only compelling reason for taking it away is laziness that'll hurt you in the long run, or if the problem/script is very small or trivial.


Who modded that down? He has a good point. This is a bad idea. In particular, "Python's static type-checking would be optional - programs can still be run even if the static checker has complaints." is a really bad idea. It means the type annotations can't be used for optimizations. Or we'll have code that only runs with optimization turned off, or on some compilers.


What if:

1. The default will be dynamic typing. But you can change that.

2. You can specify a specific module you release as static. This will only affect the internal parts of the module, with one exception - when you call something of that module with the wrong type, you'll get a warning "this runs XX times slower because of a wrong type, change..." , but when you run it with the right type, you'll get great speedup ?


What if:

1. The default will be dynamic typing. But you can change that.

2. You can specify a specific module you release as static. This will only affect the internal parts of the module, with one exception - when you call something of that module with the wrong type, you'll get a runtime type exception, so there's no problem working with dynamic code with a fast module.


What if:

1. The default will be dynamic typing. But you can change that.

2. You can specify a specific module you release as static. This will only affect the internal parts of the module, with one exception - when you call something of that module with the wrong type, you'll get a warning "this runs XX times slower because of a wrong type, change..." , but when you run it with the right type, you'll get great speedup ?


What if:

1. The default will be dynamic typing. But you can change that.

2. You can specify a specific module you release as static. This will only affect the internal parts of the module, with one exception - when you call something of that module with the wrong type, you'll get a runtime type exception, so there's no problem working with dynamic code with a fast module.


What if:

1. The default will be dynamic typing. But you can change that.

2. You can specify a specific module you release as static. This will only affect the internal parts of the module, with one exception - when you call something of that module with the wrong type, you'll get a runtime type exception, so there's no problem working with dynamic code with a fast module.


It's easy for an IDE to hide the type info, if needed, to make things more readable.Yes it's a drawback, but the benefits are quite large.


Then please, suggest a briefer, more succinct version. Explicitness has always been more important than being brief.


It's optional.


"Pythonic" is the "patriotic" of programming language world. The term is disgusting. Your language will eventually become lederhosen, beer and bratwurst or apple pie, hamburgers and the Fourth of July. It might make you feel good, but it doesn't mean anything.


Style-fetishism is so silly. There's a pragmatic case for every style of programming.


Amusingly, your pythonic example isn't even pythonic. The first line should read def foo(*args):


Why? If a, b and c are separate args why not name them? Especially if they're of different types (as per second example).


[deleted]


Sure, but isn't the comment obviously a demonstration of formal parameter syntax rather than functionality? Surely the more idiomatic Python would be not to write the function at all if it does nothing (and then there's no example code to illustrate with).


I look forward to this being back-ported to Python 2.x so that it might get used in the real world.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: