pythonpython-typingpyright

how to instantiate defaultdict to fulfill DefaultDict[int, DefaultDict[int, MyClass]] type annotation


I'm using python 3.8 and just updated pyright to 1.1.377 and I get now type errors for defaultdict(defaultdict) assignments.

The code to reproduce what I mean would be:

from typing import DefaultDict
from collections import defaultdict

class MyClass:
    pass

d: DefaultDict[int, DefaultDict[int, MyClass]] = defaultdict(defaultdict)

d[1][2] = MyClass()

I get pyright error:

error: Expression of type "defaultdict[Unknown, defaultdict[Unknown, defaultdict[Unknown, Unknown]]]" is incompatible with declared type "DefaultDict[int, DefaultDict[int, MyClass]]

How to do the defaultdict instantiation here to fulfill the type I've declared?


Solution

  • If you take a look at the docs it explains why you might assume that this should work especially if you imagine mypy and pyright are quite similar. pyright gives us instances they are not look at the docs below for some common issues with types in pyright vs mypy

    Ref: doc

    Having said that, The Issue you are having is solely because of how Pyright handles the type inference for defaultdict. When you use defaultdict(defaultdict) without specifying the type arguments, Pyright unlike mypy cannot infer the correct types, leading to the error you're seeing.

    What you can do is use good old lambda to help with the use of lambda: lambda func would returns a defaultdict with the correct type which I hope is what you want -> defaultdict[int, MyClass]

    here is a snippet to guide you

    
    from typing import DefaultDict
    from collections import defaultdict
    
    class MyClass:
        pass
    
    d: DefaultDict[int, DefaultDict[int, MyClass]] = defaultdict(lambda: defaultdict(MyClass))
    
    d[1][2] = MyClass()
    

    Here is a simple test to valid instantiation of defaultdict

    d: DefaultDict[int, DefaultDict[int, MyClass]] = defaultdict(lambda: defaultdict(MyClass))
    
    def func(a: DefaultDict[int, DefaultDict[int, MyClass]]):
        reveal_type(a)  # pyright should now reveal Runtime type is 'defaultdict'
    
    func(d)