Alex Lin Wang 王帅

Self Typing

August 15, 2025 • 6 min read
Understanding Python's Self type hint for better type safety and method chaining. Learn how to properly annotate methods that return the current instance.

Imagine you are writing a class that returns the current object in python such as when you want to allow for method-chaining in a sequence of calls, or using a setter-like method. For the type annotations here, two common ways other devs write this is:

# String forward reference
class MyBuilder:
    def __init__(self): self.name = ""
    def set_name(self, name: str) -> "MyBuilder":
        self.name = name
        return self
# Bare class name
class MyBuilder:
    def __init__(self): self.name = ""
    def set_name(self, name: str) -> MyBuilder:
        self.name = name
        return self

However, this causes two major problems where:

  • Refactoring: The return type of either the string 'MyBuilder' or class name MyBuilder will make refactoring difficult.
  • Subclassing: If you subclass, the return type is incorrect.

Instead, you can import Self as the type annotation as such:

from typing import Self  # Python 3.11+ else 'from typing_extensions import Self'
class MyBuilder:
    def __init__(self): self.name = ""
    def set_name(self, name: str) -> Self:
        self.name = name
        return self

Advantages of This Approach

  • Refactoring: No hardcoded class, which means it is resilient to class renaming.
  • Easier Subclassing: Self is covariant and dynamically binds to self at runtime. Chain methods will now return the correct subclass type.
  • Readability: Self clearly communicates to readers that we are using the same type as the instance.

Questions or feedback? Feel free to reach out!