Search code examples
pythonoperator-overloadingpython-typing

Why can you overload getattr for a typing.Dict but not a typing_extensions.TypedDict?


I am using python 3.10, and switched from Dict to TypedDict for the annotations. Following the advice of Pep 655, I'm importing from typing_extensions instead of typing; I also want NotRequired, which can only be imported from typing_extensions at this version.

I've noticed that I can overload __getattr__ (and __getattribute__) for Dict, OrderedDict, and even typing.TypedDict just fine, but that it is ignored for typing_extensions.TypedDict children. Why is this, and is there a way around it?

Example:

from typing_extension import TypedDict
from typing import Dict, TypedDict as TypedDict2

class PerfectlyFineDict(Dict):
    def __getattr__(self, key): return self[key]
class PerfectlyFineDict2(TypedDict2):
    def __getattr__(self, key): return self[key]


f = PerfectlyFineDict(a=1)
print(f['a']) # Of course, prints "1"
print(f.a) # Works perfectly fine - prints "1"

f = PerfectlyFineDict2(a=1)
print(f['a']) # Of course, prints "1"
print(f.a) # Works perfectly fine - prints "1"


class FailingDict(TypedDict):
    def __getattr__(self, key): return self[key]

f = FailingDict(a=1)
print(f['a']) # Of course, prints "1"
print(f.a) # AttributeError: 'dict' object has no attribute 'a'

I would rather not import TypedDict from typing and NotRequired from typing_extensions, since it seems to be against the PEP standard and I'm not sure how it will interact with the lexer (probably fine), but I'm not sure what the alternative is.


Solution

  • As written in PEP 589, under class based syntax

    Methods are not allowed, since the runtime type of a TypedDict object will always be just dict (it is never a subclass of dict).

    And indeed

    f = PerfectlyFineDict2(a=1)
    print(type(f))
    

    shows that it is still just a <class 'dict'>.

    You are simply only expected to use it as they describe

    class PerfectlyFineDict2(TypedDict2):
        stuff: int
        things: str
    

    PyCharm was clever enough to detect this attempt and gave a yellow squiggly indicator with a helpful link to the exact explanation.

    As far as I understand it, this class based syntax is just borrowing the syntax of inheritance, but it's really just meta-class black magic where anything goes.