Type-hinting instance variables

Not sure if I am doing this correctly, so far I have been type-hinting my instance variables by listing them under the class definition which seems to be the recommended Python way. However I am now getting concerned about whether defining them at this class level might make them class variables rather than instance variables as they are supposed to be. See example below, are these variable instance or class? In this case Python is very confusing.

class Request:
    environ: dict[str, Any]
    cookies: dict[str, str]
    queries: dict[str, str]
    form: dict[str, str]

    def __init__(self, environ: dict[str, str]) -> None:
        self.environ = environ
        self.cookies = {}
        self.queries = {}
        self.form = {}

Those would be instance variables.

Class variables are denoted by the ClassVar annotation: typing — Support for type hints — Python 3.14.7 documentation

I don’t see any problem here.

Request.environ = {‘one’:‘1’}
inst = Request({‘two’:‘2’})
print(inst.environ)
print(Request.environ)

It’s output is as expected.

{‘two’: ‘2’}
{‘one’: ‘1’}

But it has created a class variable (Request.environ) where I didn’t want one. This creates the opportunity for mistakes if that is accidentally used instead of the instance variable. For reliability print(Request.environ) should error with a NameError.

It’s not the type hints on the class that mean you can access Request.environ without raising an exception - it’s the assignment Request.environ = {‘one’: ‘1’}. If you don’t assign to that class attribute, you get exactly the same AttributeError:

from typing import Any


class RequestA:
    environ: dict[str, Any]

    def __init__(self, environ: dict[str, Any]) -> None:
        self.environ = environ


class RequestB:
    def __init__(self, environ: dict[str, Any]) -> None:
        self.environ: dict[str, Any] = environ


a = RequestA({"a": 1})
print(a.environ)
print(RequestA.environ)
# AttributeError: type object 'RequestA' has no attribute 'environ'

b = RequestB({"b": 1})
print(b.environ)
print(RequestB.environ)
# AttributeError: type object 'RequestB' has no attribute 'environ'

You mention that listing attributes on the class is the recommended Python way - I’m not sure I recognise that advice, and I would favour RequestB so that a type checker will catch the attempt to access environ on the class.