mmdanziger
mmdanziger

Reputation: 4658

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))

Upvotes: 1

Views: 471

Answers (1)

MisterMiyagi
MisterMiyagi

Reputation: 52099

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.

Upvotes: 2

Related Questions