Search code examples
pythonpython-typingpython-dataclasses

How to get callable class objects from self.__annotations__ with __future__ annotations?


What is the best way to look up the class from annotations if using py3.10 from __future__ import annotations? Previously, self.__annotations__[name] would get you an object but now it returns a string. You can use globals() as shown but that just doesn't seem right to me. Is there a better way?

@dataclass
class Person:
    height: float
    def stack(self, other: Person):
        return Person(self.height + other.height) #gymnasts
@dataclass
class Chair:
    height: float
    def stack(self, other: Chair):
        return Chair(0.6*(self.height + other.height) ) #stackable chairs
@dataclass
class Room:
    person: Person
    chair: Chair
    
    def set(self, name: str, val: float):
        # lots of complicated validation
        # factory = self.__annotations__[name] # this worked before from __future__ import annotations
        factory = globals()[self.__annotations__[name]]
        self.__setattr__(name, factory(height=val))

Solution

  • Use typing.get_type_hints to access the evaluated __annotations__ of a "function, method, module or class object". Notably, this is aware of basic inheritance – it merges inherited annotations and uses globals of the current class instead of the method's module.

    @dataclass
    class Room:
        person: Person
        chair: Chair
    
        def set(self, name: str, val: float):
            factory = get_type_hints(type(self))[name]
            self.__setattr__(name, factory(height=val))
    

    While typing.get_type_hints does some additional work over just inspecting globals, __future__.annotations inherently erases non-global information. For the uncommon case of classes defined in a locals scope one has to explicitly provide the namespace.