Question: Is it possible to make the global variable handlers a static class variable? from typing import Callable, Self # dispatch table with name to method mapping handlers:dict[str, Callable[..., None]] = {} class Foo: # Mark method as a handler for a provided request name @staticmethod def handler(name: str) -> Callable[[Callable], Callable]: def add_handler(func: Callable[..., None]) -> Callable: handlers[name] = func # This line is the problem return func return add_handler # This method will handle request "a" @handler("a") def handle_a(self) -> None: pass # Handle one request with provided name, using dispatch table to determine method to call def handle(self, name: str) -> None: handlers[name](self)  The goal is to have the decorator add the decorated method into a dict that is part of the class. The dict can then be used to dispatch methods via the name used in request messages it will receive. The problem seems to be how to refer to class data inside a decorator. Of course using self.handlers won't work, as self isn't defined. The decorator is called when the class is defined and there aren't any instances of the class created yet for self to reference. Using Foo.handlers doesn't work either, as the class name isn't defined until after the class definition is finished. If this wasn't a decorator, then handler() could be defined as a @classmethod, and then the class would be the first argument to the method. But it doesn't appear possible to make a decorator a class method. An example of how this might be used, would be as a handler for a server requests, e.g. a websocket server: from websockets.sync.server import serve def wshandler(websocket): f = Foo() # Create object to handle all requests for msg in websocket: decoded = json.loads(msg) # Decoded message will have a field named 'type', which is a string indicating the request type. Foo.handler(decoded['type']) with serve(wshandler, "localhost", 8888) as server: server.serve_forever() 

Answer: When handler is called, the name handlers will exist in the class statement's namespace, but not yet be bound as a class attribute (as you've noticed). It also is not part of any scope that handler will have access to, since the class statement does not define any scope. You can, however, inject a reference to the dict into the scope of handler by using handlers as the default value for a parameter you will otherwise never provide an argument for. Inside handle, you can access handlers as an ordinary class attribute. (Note, too, that handler does not need to be a static method, because you only use the regular function that will eventually be bound to a class attribute. You could even add del handler to the end of the class statement to prevent the attribute from being defined, because by that point you are done calling handler.) from typing import Callable, Self # dispatch table with name to method mapping class Foo: handlers: dict[str, Callable[..., None]] = {} def handler(name: str, _h=handlers) -> Callable[[Callable], Callable]: def add_handler(func: Callable[..., None]) -> Callable: _h[name] = func return func return add_handler @handler("a") def handle_a(self) -> None: pass def handle(self, name: str) -> None: self.handlers[name](self)