What is the new Self Type in Python 3.11?

What is the new Self Type in Python 3.11?

PEP 673 – Self Type

If you have been using Typehints in Python, you are already aware of how we use Types in Python, although keep in mind that the Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc.

In the freshly released Python 3.11 (2022-10-24) also known as PEP-664, so many features have been introduced such as faster CPython, Support for parsing TOML & Self Type and much more.

Let's focus on Self Type for this blog. Self Type is defined in PEP 673 – Self Type. But What is the use case of this Self type? Let's understand by an example.

class Shape:
    def set_scale(self, scale: float):
        self.scale = scale
        return self

Shape().set_scale(0.5)  # => Return type should be Shape

As given in the above example, set_scale will return the instance itself, which will allow us to do method chaining. But how do we define the return type of set_scale above?

One way to denote the return type is to specify it as the current class, say, Shape. Using the method makes the type checker infer the type Shape, as expected.

class Shape:
    def set_scale(self, scale: float) -> Shape:
        self.scale = scale
        return self

Shape().set_scale(0.5)  # => Return type is Shape

So we have got the solution, but is it really going to work if we create a subclass of Shape and use the set_scale method in that subclass? The answer is no, let's understand from the below code.

class Circle(Shape):
    # Circle is a Subclass of Shape
    def set_radius(self, r: float) -> Circle:
        self.radius = r
        return self

Circle().set_scale(0.5)  # Return type is *Shape*, not Circle
Circle().set_scale(0.5).set_radius(2.7) # => Error: Shape has no attribute set_radius

As given in the above example, the IDE will show an error on the last line because Circle().set_scale(0.5) return type is Shape and IDE is not able to parse that we are calling set_scale from Circle not Shape. Hence, the new Self needs to be introduced. Let's understand how it works.

from typing import Self  # New Self type

class Shape:
    def set_scale(self, scale: float) -> Self:
        self.scale = scale
        return self

class Circle(Shape):
    def set_radius(self, radius: float) -> Self:
        self.radius = radius
        return self

Shape().set_scale(0.5) # Return type is *Shape*
Circle().set_scale(0.5)  # Return type is *Circle*
Circle().set_scale(0.5).set_radius(2.7) # Return type is *Circle*

Above, the return type Self represents the fact that the function returns self (which is an instance of the class) and is easier to understand.

As in the above example, the type checker will correctly infer the type of Circle().set_scale(0.5) to be Circle, as expected.

This is it for today.

Any thoughts? Write it down in the comments.

For more such crispy blogs daily, follow Dev.Junction, subscribe to our newsletter and get notified.

Did you find this article valuable?

Support Dev.Junction by becoming a sponsor. Any amount is appreciated!