Where do you annotate a long type of a class attribute?

Hi! Which implementation would you choose?

from collections import defaultdict
class EventsA:
    def __init__(self):
        self.event_handlers: Dict[str, List[Callable]] = defaultdict(list)
class EventsB:
    def __init__(self):
        self.event_handlers: Dict[str, List[Callable]]
        self.event_handlers = defaultdict(list)
class EventsC:
    def __init__(self):
        self.event_handlers = defaultdict(list)
        self.event_handlers: Dict[str, List[Callable]]

I am leaning towards EventsB but would like to hear what other Pythonistas would choose.

I think (A) and (B) are both fine. (A) is a bit more terse but sometimes it’s useful to separate “declaration” from “definition” (in c parlance), especially when you have several of them, or they are individually long and complicated. This one is borderline, to me.

In fact (C) is actually incorrect typing syntax (annotation must precede first use):

$ mypy ./events_type_test.py
events_type_test.py:13: error: Attribute "event_handlers" already defined on line 12 [no-redef]
Found 1 error in 1 file (checked 1 source file)

A few only vaguely related points:

  • You can use dict and list instead of Dict and List, but you do need from typing import Callable.
  • You need to either annotate __init__(self) -> None or call mypy --check-untyped-defs

To type attributes, I often put the annotations outside of the __init__ method:

class Foo:
    baz: int

    def bar(self):
        return self.baz

But as you can see I typically only do that for attributes not initialized in __init__, not because the type annotation gets unwieldy. If the type annotation gets too big, and I use it more than a couple of times, I tend to create a type alias for it.

In your case I would prefer A, separating the annotation from the “declaration” can cause them to drift apart in the code making it annoying to change things later

Personally I like having the annotation at the module level so it can be reused by outside users:

EventHandlers: TypeAlias = Dict[str, List[Callable]]

class Events:
    def __init__(self):
         self.event_handlers: EventHandlers = defaultdict(list)
1 Like