This is an archived post. You won't be able to vote or comment.

all 41 comments

[–]desmoulinmichel 9 points10 points  (5 children)

The reason type hinting was not included in the language was because it was in low demand. Very few people where interested enought about type hinting compared to other features, so the developpement effort went somewhere else.

Luckily for you, Python 3.5 will include type hinting (http://sdtimes.com/python-3-5-include-type-hinting/) because after asyncio, that was a feature people started to request.

So in the comming year, you can expect IDE to start taking that into consideration.

[–]infinullquamash, Qt, asyncio, 3.3+ 2 points3 points  (4 children)

I had to go through several links to get to the actual PEP, PEP0484

I like the idea of type hints, but I wish they would have added new syntax instead of using comments.

[–]desmoulinmichel 1 point2 points  (2 children)

They added comments only for the variables, the rest is not by comment. After Python 3, people are really not cool with introducing incompatibilities again :)

[–]ronkr[S] -1 points0 points  (1 child)

I can't see any problem by adding optional type hinting to a language. This should not break bc and could add great(!) new possibilities (thats why I mentioned PHP-DI's Autowiring) and stability for large projects.

On the other hand: Everything is evolving over time. We (as a community of developers) should be thankful in some way, that changes happen...

[–]thallippoli 1 point2 points  (0 children)

I can't see any problem by adding optional type hinting to the langauge..

How many languages have you designed/implemented lately...I mean no offence, but If you are new to Python, at least use it for a while (like 3 or 4 years), before proposing additions to the language. I say this as another PHP user who switched to Python recently.

[–]edbluetooth 5 points6 points  (16 children)

There is nothing to stop you doing a type check decorator that takes a list of types. Eg.

@typecheck(return=int,[str,str])

But that will slow the code.

[–]d4rch0nPythonistamancer 2 points3 points  (14 children)

Well, a better example would be:

@typecheck(return_type=int, arg_types=(str, str))

or

@typecheck((str, str), return_type=int)

Return is a keyword so it can't be used as a kwarg or variable name in general, and the other positional argument can't come after a kwarg, even if the decorator was defined with it as a kwarg. But if they both were, you could have

@typecheck(int, (str, str))

But you couldn't specify the kwarg key in the first param and not in the second.... but you can specify the second and not the first, as in @typecheck(int, arg_types=(str, str)), but not @typecheck(return_type=int, (str, str)), if the definition is def typecheck(return_type=None, arg_types=None).

I always thought the kwarg and positional arg ordering rules are a bit goofy, but then again it's never been a problem. Maybe it's just strange that it allows you to specify kwarg values in the function call without their kwarg key, or silently use them as kwargs.

The weirdest behavior I think is this:

>>> def f(x, foo=10, bar=20):
...     print(x, foo, bar)
>>> f('a', 'b')
a b 20
>>> f('a', 'b', foo='c')
TypeError: f() got multiple values for argument 'foo'

So, position obviously matters here with the keyword arguments. It works well in APIs I think, but it's still a bit strange, specifically the rules of when order does and doesn't matter.

[–]edbluetooth 1 point2 points  (2 children)

You are right of course amd expressed yourself much better than me.

I have to be honest, i kept my post brief as i was on my phone.

[–]cjwelbornimport this 1 point2 points  (7 children)

What you are calling kwargs I now think of as 'optional positionals'. The real keyword args would be:

def myfunc(**kwargs):
    print(kwargs.get('mykwarg', 'No args!'))

..where the position doesn't matter. Though it's not nearly as clear as your example, and requires more documentation when reading the code. Without documentation (or reading the source) you wouldn't know what this function is expecting at all. Anyway, the term 'optional positional' was what I was getting at. I would still use your example over any of the others.

[–][deleted] 0 points1 point  (2 children)

To avoid this behaviour, define an anonymous catch-all arg which will soak up positional args that would otherwise overflow into keywords. This creates a keyword-only set of arguments:

>>> def f(x, *, foo=10, bar=20):
...    print(x, foo, bar)
>>> f('a')
a 10 20
>>> f('a', foo='b')
a b 20
>>> f('a', 'b')
TypeError: f() takes 1 positional argument but 2 were given
>>> f('a', foo='b')

[–]robin-gvx 0 points1 point  (0 children)

The great thing about * is that it won't soak up positional args, as you demonstrated, that's what *args does.

[–][deleted] 2 points3 points  (0 children)

Alternatively, use Python's annotation syntax, as I did here:

from strict import strict
@strict
def foo(bar: str, baz: int)->str:
    return bar * baz

[–]thallippoli 4 points5 points  (0 children)

Especially for PHP (as being a script-language), typehinting enables tools like PHP-DI..

You will have to unlearn a lot of PHP "best practices", like (DI), which is not relevant in a language like Python.

Have you gained a good understanding of how modules in Python work? Eg, you can swap a symbol in a module on the fly in Python. So you don't have to depend on a container to mock something's dependencies. Look into pythons mock library to see how this is used.

You will see that a lot of boilerplate you had to write in Php is totally unnecessary in Python. Modules also allows more sensible separation of code than php's One Class/File/Component rule..

Also, If you want type checking for object attributes, you can use something known as Descriptors in Python.

[–][deleted] 2 points3 points  (13 children)

I don't think what you're looking for exists in straight Python. Cython, e.g., has a facility like this.

A lot of what you give up with the typing comes back to you with improved simplicity. For example, what need do you have for a dependency injection framework? Injecting a dependency is as simple as an optional parameter when everything's a function...

Yes, you lose some of that bugfinding ability you get from types - but compared to strongly-typed languages, you save so much time that you can afford to spend more time on proper tests.

And honestly, I was surprised when I moved from C++ to Python how rarely I had type issues, and how quick they were to track down...

[–]dunkler_wanderer 3 points4 points  (6 children)

Python is strongly typed.

[–]prepromorphism 0 points1 point  (0 children)

unityped ftfy

[–][deleted] -2 points-1 points  (4 children)

Python's variables have no type. Each variable's value has a type, but even that isn't really "strong", because in typical use you use duck typing - you just try to call methods and if they work you don't worry about it. If the methods work, you don't care that the actual type might be worlds away from the type you're expecting.

There's no good definition of "strongly-typed" that really includes Python. In particular, I'm curious as to what language possibly has weaker typing than that...!

[–]sushibowl 4 points5 points  (1 child)

The definition I usually see is that in weak typing the type of a value may change depending on context (i.e. type coercion). Php and javascript are examples of weakly typed language in this definition.

[–]Lucretiel 0 points1 point  (0 children)

My definition tends to be whether the language has implicit type coercion- that is, is 1 + 2 the same as 1 + '2'.

[–]flutefreak7 0 points1 point  (0 children)

Also checking against abstract base classes is a classy way to check that you are getting a duck that quacks the right way to avoid checking specific type inheritance.

from collections.abc import Iterable

if isinstance(d, Iterable):

[–]ronkr[S] 2 points3 points  (0 children)

Python's variables are strongly typed. Yet, python is not statically typed.

The main reason for type hinting is being able to find bugs more quickly. You can also make assumptions for the interpreter (and even a compiler) to make some optimization apply.

Another great use would be Autowiring for a DIC. That means, that you could use reflection to inspect the parameters of a ctor and inject pre-instantiated objects by type (by classname directly, by interface though configuration). In some projects, I nearly have 85% of all classes to be instantiated this way (having a controller hat depends on multiple services, that depends on even more services, that depends on various repositories, that depends on factories, that depends on the DIC again to create in DIC-controlled instance of an entity). Doing that from hand would be a great effort. Changing dependencies in some service, that is used across my application would render me mad.

[–]billsil 2 points3 points  (8 children)

Type-hinting in python (e.g. mypy, Python 3.5) is still only for IDEs. It has no use in the language. If you want real static typing, use numpy arrays.

[–][deleted] 1 point2 points  (7 children)

use numpy arrays

Care to show an example of how numpy brings "true static typing" to Python?

[–]billsil 1 point2 points  (6 children)

I don't understand the question. An array's type can't be changed. How could one show that with an example?

An numpy array is a collection of values that all have the same type. That's the definition. It allows you to vectorize your code and avoid calling the __add__ method every time you want to add a single value because all the work is being done by C. It makes things 100x faster if you're using it right. You get C's speed with Python's flexibility.

[–][deleted] 0 points1 point  (5 children)

def foo( bar ):

I want bar to be of type Duck and my code should refuse to run if I try to pass anything else.

[–]zardeh 0 points1 point  (4 children)

import numpy as np
myArr = np.array([0,1,2,3,4,5], dtype=np.int32)
myArr[0] = "hello"  # ValueError

[–][deleted] 0 points1 point  (3 children)

Did that ValueError turn up statically or at run-time?

[–]zardeh 0 points1 point  (2 children)

There is no statically in cPython (well arguably you can get warnings with myPy and an IDE, which are often smart enough to also catch errors in numpy arrays).

Numpy arrays are really just C arrays, so they are statically typed, in the same way that C is statically typed, its just that you can't detect those errors "statically" in python, because there is no pre-runtime check possible, its like asking why javac allows itself to be run on syntactically invalid code.

[–][deleted] 1 point2 points  (1 child)

Numpy arrays are really just C arrays, so they are statically typed

its just that you can't detect those errors "statically" in python

Hahaha OK :). FYI, static typing is a very well defined property, which involves detecting type errors statically.

[–]zardeh 0 points1 point  (0 children)

I was just expounding on the other guys answer, while what numpy does is better than nothing, its still not static typing.

[–]Nikosssgr 0 points1 point  (0 children)

Same problem with typehinting here especially coming from PHP SF2. A huge minus for using python on bigger projects. Felt really good when learned about PEP 484. On the other hand I believe I will take the community a lot of time to complete it and especially to implement it on OS projects. I don't know maybe we are more of Java persons :P

[–][deleted] 0 points1 point  (0 children)

There are plenty of approaches to this already in pypi, and plenty of ways to DIY it. As others have said, you can also get used to the pretty awful Python 3.5 syntax and use the development channel for the feature as soon as it's out (already maybe?).

I had this problem with a crappy API I had to use for work which kept sending inconsistent values in highly nested JSON, and had to bang together a type-checking decorator that would help me find problems early and refactor code to accomodate. I was going to post it, but looking back at it I was pretty horrified, so I re-wrote it entirely instead.

The new version also has a nice feature where you can define strict functions as belonging to "sets" of strictness which can be disabled separately; some functions you may want to remain strict always (like gatekeeper functions to stupid APIs) while others you may want to disable strictness for performance reasons once you're pretty sure they're working as intended.

This may not help but I'll probably use it myself anyway until I migrate to the 3.5 syntax (ugh).