Search code examples
pythonlisttype-hintingtyping

How to type hint a list with a particular structure


I have a data structure in my code that (for the sake of an MWE) is a list where the first element is a string, and the second element is an integer. For example:

foo: MyStructure = ["hello", 42].

Now, since there is an ordering to this structure, usually I would use a tuple and instead do:

foo: Tuple[str, int] = ("hello", 42).

But I explicitly want to be able to easily modify elements within the structure. In particular, I want to be able to set foo[0] = "goodbye", which cannot be done if foo is a tuple.

What is the best way to go about typing this structure?

(I don't think that this question is opinion-based, since I think there is likely clear rationale for how to handle this that would be preferred by most developers.)

Right now, the main solution I can think of is to not actually type the structure correctly, and instead to define my own structure whose true type is listed in a comment:

# MyStructure = [str, int]
MyStructure = List[Union[str, int]]

foo: MyStructure = ["hello", 42]

Is there a better way?


Solution

  • You don't want a list or a tuple; you want a custom class representing the type-level product of str and int. A dataclass is particularly useful here.

    from dataclasses import dataclass
    
    
    @dataclass
    class MyStructure:
        first: str
        second: int
    
    
    foo: MyStructure = MyStructure("hello", 42)
    
    assert foo.first == "hello"
    assert foo.second = 42
    

    If you really want to access the components using integer indices, you can add a __getitem__ method to the class:

    @dataclass
    class MyStructure:
        first: str
        second: int
    
        def __getitem__(self, key) -> Union[str,int]:
            if key == 0:
                return self.first
            elif key == 1:
                return self.second
            else:
                raise IndexError(key)
    

    In addition, an instance of MyStructure uses less memory than the corresponding list:

    >>> foo = MyStructure("hello", 42)
    >>> import sys
    >>> sys.getsizeof(foo)
    48
    >>> sys.getsizeof(["hello", 42])
    72