Search code examples
pythonlist-comprehensiondictionary-comprehensionmypy

mypy and nested dict comprehension


I'm definitely not an expert of mypy, but there's an error that I'm really not understanding.

Let's say that I have this dictionary and I want to parse it and create another one through a dict comprehension.

my_dict = {
    'type1': {
        'category1': [
            'subcategory1',
            'subcategory2',
        ],
    },
    'type2': {
        'category2': [
            'subcategory3',
        ],
        'category3': [],
    },
}

The dict comprehension:

new_dict = {
    subcategory: {
        'category': category,
        'type': type,
    }
    for type, categories in my_dict.items()
    for category, subcategories in categories.items()
    for subcategory in subcategories
}

and the expected output:

{
    'subcategory1': {
        'category': 'category1',
        'type': 'type1'
    },
    'subcategory2': {
        'category': 'category1',
        'type': 'type1'
    },
    'subcategory3': {
        'category': 'category2',
        'type': 'type2'
    }
}

mypy in this situation complains because of the empty category3, but with an error message ('error:"object" has no attribute "items"') that refers to the previous line.

Any suggestion?

Thanks in advance


Solution

  • The issue is mypy is unable to infer the type of my_dict -- it's too complicated for mypy to naturally infer the type.

    You can confirm for yourself by adding the line reveal_type(my_dict) before running mypy. (Mypy special-cases that function name to help with debugging). The inferred type ended up being Dict[str, object], or something to that effect.

    You can fix this by explicitly giving my_dict a type. If you're using Python 3.6+, you can use the new variable annotation syntax to do so, like so:

    from typing import Dict, List
    
    my_dict: Dict[str, Dict[str, List[str]]] = { ... }
    

    If you're using earlier versions of Python, annotate the variable using the comment-based syntax.