Python 3中如何为__getattr__与__getattribute__方法添加返回值类型注解?
Great question! You're right that PyCharm (and most type checkers like mypy) don't automatically infer the type of dynamically accessed attributes via __getattr__ or __getattribute__ out of the box. Let's break down why your initial attempts didn't work, then walk through reliable solutions.
Why Your Initial Code Didn't Work
Type checkers don't assume every undefined attribute on your class will return the type specified in __getattr__—they can't predict all possible attribute names you might access, and __getattr__ only triggers for attributes that don't exist on the class/instance.
Your second attempt using Callable[[], str] was misaligned with the actual signature of __getattr__: it expects two arguments (self and the attribute name item), not zero. That's why the type hinting still failed.
Solution 1: Use a Protocol to Define Dynamic Attribute Types
The cleanest way to tell type checkers that any dynamic attribute on your class returns a str is to define a Protocol that describes this behavior, then annotate your instance with that protocol.
from typing import Protocol # Define a protocol enforcing any dynamic attribute returns a str class DynamicStrAttrs(Protocol): def __getattr__(self, item: str) -> str: ... class MyClass(dict): def __getattr__(self, item: str) -> str: # Your actual logic here—e.g., fall back to dict items or return a default return self.get(item, 'test') # Optional: Override existing attributes too with __getattribute__ def __getattribute__(self, item: str) -> str: try: # First check for existing attributes/methods return super().__getattribute__(item) except AttributeError: # Delegate to __getattr__ for dynamic attributes return self.__getattr__(item) # Annotate the instance with our protocol c: DynamicStrAttrs = MyClass(hello=123) print(c.hello) # PyCharm will now show this as type `str` print(c['hello']) # Your __getitem__ type hinting remains functional
Solution 2: Use Generic for Type-Aligned Dict Subclasses
If your use case requires the dynamic attribute type to match the values stored in your dict (instead of a fixed str), use Generic to create a type-safe dict subclass:
from typing import TypeVar, Generic, Dict T = TypeVar('T') class TypedDictWithAttrs(Dict[str, T], Generic[T]): def __getattr__(self, item: str) -> T: return self[item] def __setattr__(self, item: str, value: T) -> None: self[item] = value # Create an instance where all values are strings c = TypedDictWithAttrs[str](hello='world', foo='bar') print(c.hello) # Type hint shows `str` print(c.foo) # Type hint shows `str`
Solution 3: Quick Fix with cast (For One-Off Access)
If you don't want to set up a protocol or generic class, use cast to explicitly tell the type checker the attribute's type for individual accesses:
from typing import cast c = MyClass(hello=123) hello_value = cast(str, c.hello) print(hello_value) # Type hint works here
内容的提问来源于stack exchange,提问作者Wizard.Ritvik

