12

When type annotating a variable of type dict, typically you'd annotate it like this:

numeralToInteger: dict[str, int] = {...}

However I rewrote this using a colon instead of a comma:

numeralToInteger: dict[str : int] = {...}

And this also works, no SyntaxError or NameError is raised.

Upon inspecting the __annotations__ global variable:

colon: dict[str : int] = {...}
comma: dict[str, int] = {...}

print(__annotations__)

The output is:

{'colon': dict[slice(<class 'str'>, <class 'int'>, None)],
 'comma': dict[str, int]}

So the colon gets treated as a slice object and the comma as a normal type hint.

Should I use the colon with dict types or should I stick with using a comma?

I am using Python version 3.10.1.

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Magmurrr
  • 238
  • 1
  • 9
  • 1
    Type hints can be anything. What determines the effectiveness is how it interacts with your editor, or whatever software interprets them. – Mad Physicist Jan 20 '22 at 22:36
  • 5
    Since the implied use of the dict is to translate strings to integers, that means that the dict has keys of type `str` and values of type `int`. So the comma seperation is the correct type hint in this case. Or are your keys slices? I presume not. – Richard Neumann Jan 20 '22 at 22:37
  • 1
    @RichardNeumann In terms of readability it makes sense to see dict[str : int] for example as the colon "connotes" that it maps a str to an int as it follow the literal dict syntax. Im guessing you're looking at the documentation that states values in typehints should be seperated by commas. As Mad Physicist stated and the documentation also outlined they have no effect on actual python code so this just makes it purely preference on how you want to style it. The better question for me to ask is "What convention should I stick to?" – Magmurrr Jan 20 '22 at 22:45
  • @Magmurrr No, it does not. It creates a `slice` object. That is not how this type hint is supposed to be used. If you use a type checker, it will yell at you, that the keys are not meant to be strings but slices. – Richard Neumann Jan 20 '22 at 22:47

2 Answers2

9

If you have a dictionary whose keys are strings and values are integers, you should do dict[str, int]. It's not optional. IDEs and type-checkers use these type hints to help you. When you say dict[str : int], it is a slice object. Totally different things.

Try these in mypy playground:

d: dict[str, int]
d = {'hi': 20}

c: dict[str: int]
c = {'hi': 20}

message:

main.py:4: error: "dict" expects 2 type arguments, but 1 given
main.py:4: error: Invalid type comment or annotation
main.py:4: note: did you mean to use ',' instead of ':' ?
Found 2 errors in 1 file (checked 1 source file)

Error messages are telling everything

S.B
  • 13,077
  • 10
  • 22
  • 49
  • I should have rephrased my question as I was asking purely from a stylistic perspective and not about the type-hint correctness. I don't use an IDE only a text editor so I don't worry about a type-checker. I'll accept this and leave this question up in case anyone else is confused about the underlying difference! – Magmurrr Jan 20 '22 at 22:59
  • If `str: int` is a slice object, then is `str, int` a tuple object? Or does the comma mean something different in type hints? – Kelly Bundy Jan 28 '22 at 10:04
  • @KellyBundy The context which they are in, matters. The slice object is created because `str: int` is inside the subcript notation - brackets. likewise `x, y` is not a tuple if it is inside the parenthesis of a function call. But I'm not confident to say yes here `str, int` is a tuple. It *can* be of course(implementation of `__getitem__` is up to us). Maybe type metaclass implemented `__getitem__` to accept tuples for dict and turns them to type hints. – S.B Jan 28 '22 at 10:33
  • But they're in the same context. The `str, int` isn't inside the parenthesis of a function call, it's inside the subscript notation - brackets - just like the `str: int` is. I don't know whether type hints have their own rules, but in "ordinary" code, that makes it a tuple (at least that's what `__getitem__` gets then). – Kelly Bundy Jan 28 '22 at 10:43
  • Disassembling shows that in "ordinary" code, it indeed [builds a tuple](https://tio.run/##K6gsycjPM7YoKPr/PzO3IL@oRCEls5gLiPWAWEM9MTpJRyE5Vl3z/38A). I'm wondering whether type hints have their own rules and don't make it a tuple, or whether they do make it a tuple but then handle that... – Kelly Bundy Jan 28 '22 at 10:53
  • @KellyBundy I'm [here](https://github.com/python/cpython/blob/main/Lib/typing.py#L1033), In most classes, Generic types there is a check for tuples. – S.B Jan 28 '22 at 10:57
  • 1
    Ha, I thought Python itself discards type hints, but it doesn't and [disassembly](https://tio.run/##K6gsycjPM7YoKPr/PzO3IL@oRCEls5gLiPWAWEM9xQrITy6JLi4p0lHIzCuJVdf8/x8A) shows it does build a tuple in type hints and what happens overall. – Kelly Bundy Jan 28 '22 at 10:57
3

With dict[str:int] the hint you are passing is dict whose keys are slices, because x:y is a slice in python.

The dict[str, int] passes the correct key and value hints, previously there also was a typing.Dict but it has been deprecated.

ljmc
  • 4,830
  • 2
  • 7
  • 26
  • 1
    Likewise, `x,y` is a *tuple* in Python. So by that logic, `dict[str, int]` doesn't pass the correct key and value *hints* but just *one* hint, a tuple. Right? – Kelly Bundy Jan 28 '22 at 10:08
  • They count as one hint yes as the dict's `__class_getitem__()' expected two positional arguments. – ljmc May 12 '22 at 16:32