267

I am practicing using type hints in Python 3.5. One of my colleague uses typing.Dict:

import typing


def change_bandwidths(new_bandwidths: typing.Dict,
                      user_id: int,
                      user_name: str) -> bool:
    print(new_bandwidths, user_id, user_name)
    return False


def my_change_bandwidths(new_bandwidths: dict,
                         user_id: int,
                         user_name: str) ->bool:
    print(new_bandwidths, user_id, user_name)
    return True


def main():
    my_id, my_name = 23, "Tiras"
    simple_dict = {"Hello": "Moon"}
    change_bandwidths(simple_dict, my_id, my_name)
    new_dict = {"new": "energy source"}
    my_change_bandwidths(new_dict, my_id, my_name)

if __name__ == "__main__":
    main()

Both of them work just fine, there doesn't appear to be a difference.

I have read the typing module documentation.

Between typing.Dict or dict which one should I use in the program?

Alex Waygood
  • 6,304
  • 3
  • 24
  • 46
joe
  • 8,383
  • 13
  • 61
  • 109
  • 19
    Note that Python doesn't actually *enforce* type hints. They are *just hints*, they are not used at runtime, or even compile time, to enforce types. Python may be strongly typed (opposite of weak typing), it is also *dynamically typed* (opposite of strict typing). See [Is Python strongly typed?](http://stackoverflow.com/q/11328920). External tools like mypy can use these hints to help you write better code, however, in a process called static analysis. – Martijn Pieters May 07 '16 at 11:04
  • 3
    @MartijnPieters I used to love using type hints in my code alongside MyPy and pretending I could use Python with type safety. Unfortunately it got me A) code that doesn't work on < 3.4 and B) people laughing at me because apparently, type hints are a laughingstock. It's really quite unfortunate. – cat May 07 '16 at 15:40
  • 19
    @cat: Type hinting was introduced to Python by a Facebook employee, because we had *enormous* success with adding the same feature to PHP (see [hack](http://hacklang.org/)). Anyone laughing has never built a big project with more than a handful of engineers. – Martijn Pieters May 07 '16 at 15:44
  • @cat: MyPy supports python 3.2 and up, as well as 2.7, by the way. – Martijn Pieters May 07 '16 at 15:47
  • 3
    @MartijnPieters No, `def a(b: int) -> bool:` is a syntax error in Python 2.7, and I think it's a syntax error in older versions of Python 3 too. – cat May 07 '16 at 15:49
  • @MartijnPieters I daresay anything except PHP would be better than PHP, but Hack seems to just try to make PHP even more Java++-y than the PHP devs already are. Not really on-topic, though :) – cat May 07 '16 at 15:51
  • 3
    @cat: you are talking about *function annotations* here, syntax that was added to Python 3.0. So the only version where that is a syntax error is 2.7, which is why mypy supports putting that information in comments. – Martijn Pieters May 07 '16 at 16:58
  • @cat: and indeed, we are now well into chatroom territory. Find me in the Python room if you are interested. Hack does not make PHP Java++-y; there is no static typing here. It makes PHP more like *Python*, if anything. Too verbose, still though. – Martijn Pieters May 07 '16 at 16:59

4 Answers4

324

There is no real difference between using a plain typing.Dict and dict, no.

However, typing.Dict is a Generic type * that lets you specify the type of the keys and values too, making it more flexible:

def change_bandwidths(new_bandwidths: typing.Dict[str, str],
                      user_id: int,
                      user_name: str) -> bool:

As such, it could well be that at some point in your project lifetime you want to define the dictionary argument a little more precisely, at which point expanding typing.Dict to typing.Dict[key_type, value_type] is a 'smaller' change than replacing dict.

You can make this even more generic by using Mapping or MutableMapping types here; since your function doesn't need to alter the mapping, I'd stick with Mapping. A dict is one mapping, but you could create other objects that also satisfy the mapping interface, and your function might well still work with those:

def change_bandwidths(new_bandwidths: typing.Mapping[str, str],
                      user_id: int,
                      user_name: str) -> bool:

Now you are clearly telling other users of this function that your code won't actually alter the new_bandwidths mapping passed in.

Your actual implementation is merely expecting an object that is printable. That may be a test implementation, but as it stands your code would continue to work if you used new_bandwidths: typing.Any, because any object in Python is printable.


*: Note: If you are using Python 3.7 or newer, you can use dict as a generic type if you start your module with from __future__ import annotations, and as of Python 3.9, dict (as well as other standard containers) supports being used as generic type even without that directive.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 2
    Useful additional examples would be when the dictionary values can be different types e.g. `{"name": "bob", "age" : 51}`, would that be something like`typing.Mapping[Union[str, int]` ? What about a nested dictionary like `{"person": {"name":"bob", "age": 51}` would that be something like `typing.Mapping[str, typing.Mapping[Union[str, int]]` ? Using `Union` like that troubles me because it's not a strict schema as there is no ordering. Maybe that's OK, or is there an alternative? – Davos Jun 04 '18 at 15:07
  • 1
    Nevermind about the `Union` question I see it's still an open discussion https://github.com/python/typing/issues/28 – Davos Jun 04 '18 at 15:15
  • 2
    This seems to be very interesting, useful and related https://www.python.org/dev/peps/pep-0589/ – Greg Hilston Aug 21 '19 at 15:08
  • @GregHilston: that's actually about how to restrict what keys a dictionary can contain, and specify what types each associated value should have. – Martijn Pieters Aug 21 '19 at 15:50
  • @MartijnPieters "specify what types each associated value should have." isn't that exactly what Davos is talking about above? – Greg Hilston Aug 21 '19 at 16:20
  • 2
    @GregHilston: ah, yes, it is. – Martijn Pieters Aug 21 '19 at 16:50
  • I've also been told that `Mapping[A, B]` can accept a `Mapping[A, B1]` where `issubclass(B1, B)`, while a `Dict[A, B]` can't! – xjcl Dec 08 '20 at 00:41
  • @xjcl: that's right, see the definitions for [`typing.Mapping`](https://docs.python.org/3/library/typing.html#typing.Mapping) (`VT_co` = *value type, covariant*) and [`typing.Dict`](https://docs.python.org/3/library/typing.html#typing.Dict) (`VT` = *invariant*). It's about what the function _might_ do. `Mapping` is immutable, so a function that takes a mapping with both `B` and `B1` values will work if it sticks to treating everything as `B`. But `Dict` allows *mutation*; a function that takes a dict with `B` values only inserts `B` values, so passing in `Dict[A, B1]` would break other code! – Martijn Pieters Dec 13 '20 at 12:26
  • 1
    @xjcl: see See https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance for more. – Martijn Pieters Dec 13 '20 at 12:26
  • @xjcl: that doesn't mean you can't pass in `{key: Bsubclass()}` to such a function, just that the dictionary there is annotated as `Dict[A, B]` and all other code treats the values only as `B()` instances, and not, without explicit checks, use any `Bsubclass`-specific attributes. – Martijn Pieters Dec 13 '20 at 12:33
  • 4
    Per the samples in the typing documentation `dict[str, str]` is also valid, see: https://docs.python.org/3/library/typing.html#type-aliases – haridsv Dec 31 '21 at 16:11
  • Isn't it so that Dict is being deprecated in favor of dict BUT dict will give an error in 3.8. You need at least 3.9. At least my program wont run on 3.8 if I have a dict or list typing hint. – Brian Reinhold Oct 31 '22 at 12:54
  • @BrianReinhold: You can use `from __future__ import annotations` in Python 3.8, at which point the interpreter will stop trying to execute the annotations. Type checkers can still access the annotations and they all recognise `dict[str, str]` annotations just fine. Or you can use a forward reference, by enclosing the annotation in quotes, so `"dict[str, str]"`. – Martijn Pieters Nov 25 '22 at 16:42
39

typing.Dict is a generic version of dict:

class typing.Dict(dict, MutableMapping[KT, VT])

A generic version of dict. The usage of this type is as follows:

def get_position_in_index(word_list: Dict[str, int], word: str) -> int:
     return word_list[word]

Here you can specify the type of key and values in the dict: Dict[str, int]

AKS
  • 18,983
  • 3
  • 43
  • 54
15

as said in python org:

class typing.Dict(dict, MutableMapping[KT, VT])

A generic version of dict. Useful for annotating return types. To annotate arguments it is preferred to use an abstract collection type such as Mapping.

This type can be used as follows:

def count_words(text: str) -> Dict[str, int]:
    ...

But dict is less general and you will be able to alter the mapping passed in. In fact, in python.Dict you specify more details.

Another tip:

Deprecated since version 3.9: builtins.dict now supports []. See PEP 585 and Generic Alias Type.

Att Righ
  • 1,439
  • 1
  • 16
  • 29
Ehsan
  • 3,711
  • 27
  • 30
7

If you're coming from google for TypeError: Too few parameters for typing.Dict; actual 1, expected 2, you need to provide a type for both the key and value.

So, Dict[str, str] instead of Dict[str]

snowskeleton
  • 644
  • 7
  • 7