Search code examples
pythontypeshint

Type hint for custom class atributes


I'm quite new to using type hints in python. I have an app with multiple modules in a package and classes associated to them. I've been trying to find some type hint explanation of how it works when there are multiple scripts and the type defined comes from an object whos script is loaded in another module. Here is a very simplified version for this confusion on the use of type hints.

Given that there is a script for the main app like this:

from modules.FigureFormatter import FigureFormatter
from modules.Plotter import Plotter

class MainApp:
    def __init__(self):
        formatter = FigureFormatter()
        plotter = Plotter()
        plotter.plot_figure(formatter.styler['light'])

The modules package contains two modules:

class FigureFormatter:
    def __init__(self):
        self.styler = {'light': {'prop1': 1,
                                 'prop2': 2},
                       'dark': {'prop1': 1,
                                'prop2': 2}}

and

from typing import Dict

class Plotter:
    def __inti__(self):
        # Some initialization stuff
        pass

    def plot_figure(self, styler: Dict):
        # Plotting figure
        pass

What should be the type hint for the styler argument in plot_figure method? Essentially it is a dictionary. Obviously it shouldn't be any dictionary, but a dictionary that is an attribute of the FigureFormatting class instance. Should that module be also imported into Plotter modules, so the class name could be referenced?


Solution

  • Python 3.8 introduced a TypedDict hint, which can specify a dictionary with specific str-valued keys mapped to specific types. For example:

    # In modules.FigureFormatter
    from typing import TypedDict
    
    
    StylerProperties = TypedDict('StylerProperties', prop1=int, prop2=int)
    StylerType = TypedDict('Styler', light=StylerProperties, dark=StylerProperties)
    
    # In modules.Plotter
    from modules.Formatter import StylerType
    
    
    class Plotter:
        ...
        
        def plot_figure(self, styler: StylerType):
            ...
    

    You can also use it TypedDict as a base class, which the documentation suggests is the intended use. (The called version appears to exist to support versions of Python prior 3.6, which don't allow for variable annotations. Note that TypedDict was in the experimental typing_extensions before being promoted to typing.)

    class StylerProperties(TypedDict):
        prop1: int
        prop2: int
    
    
    class Styler(TypedDict):
        light: StylerProperties
        dark: StylerProperties
    

    It doesn't make much sense to further require that the dict comes from a particular class attribute, as being referred to by an attribute doesn't change the dict value. If being an attribute of FigureFormatter is important, then just require an instance of FigureFormatter and extract the dict yourself:

    def plot_figure(self, f: FigureFormatter):
        styler = f.styler
        ...