pythonpython-typing

Sub-attribute annotation syntax


It is possible to type the following code in Python3.12:

class B:
    pass

b = B()

class A:
    a: "x"
    b.c: "y"

Without b declaration, this code throws NameError. After that, A.__annotations__ shows: {'a': 'x'}. Annotation dicts of b and B are empty.

Does this mean anything? Is it just some future syntax with no effects or does the b.c annotation end up somewhere?


Solution

  • Annotations for object.attribute, object[item] and object[sl:ice] are permitted by the grammar:

    assignment:
        | NAME ':' expression ['=' annotated_rhs ] 
        | ('(' single_target ')' 
             | single_subscript_attribute_target) ':' expression ['=' annotated_rhs ]
    
    single_target:
        | single_subscript_attribute_target
        | NAME 
        | '(' single_target ')' 
    
    single_subscript_attribute_target:
        | t_primary '.' NAME !t_lookahead 
        | t_primary '[' slices ']' !t_lookahead 
    

    They don't have any standardized semantics in the type system, however:

    The target of the annotation can be any valid single assignment target, at least syntactically (it is up to the type checker what to do with this):

    class Cls:
        pass
    
    c = Cls()
    c.x: int = 0  # Annotates c.x with int.
    c.y: int      # Annotates c.y with int.
    
    d = {}
    d['a']: int = 0  # Annotates d['a'] with int.
    d['b']: int      # Annotates d['b'] with int.
    

    § Annotating expressions | PEP 526: Syntax for Variable Annotations

    The runtime data is also not stored anywhere, as with annotations for local variables.

    One exception is self.attribute, but annotations of this kind cannot be placed directly within the class's body:

    class C:
        def __init__(self) -> None:
            self.attribute: str
    
    reveal_type(C().attribute)  # str