Exploring Typing Extensions for Modern Python Development with Examples

Introduction to typing-extensions

Python is a versatile and dynamic programming language, constantly evolving to add more features and enhancements. One of the game-changing additions to Python has been the introduction of type hints in Python 3.5, which provide optional type checking for better code clarity and error prevention. However, not all type hinting features are immediately available in every Python version. This is where the typing-extensions module comes in. It backports new type hinting features to older Python versions, making your code compatible without sacrificing functionality.

Key APIs from typing-extensions with Examples

The typing-extensions library provides dozens of useful tools to enhance type hinting in Python. Let’s explore some of the most important features with practical examples.

1. @final

The @final decorator indicates that a method or class cannot be overridden or subclassed.

  from typing_extensions import final

  @final
  class ImmutableClass:
      pass

  @final
  def cannot_override():
      print("This method cannot be overridden.")
  

2. Literal

Literal is used to define a narrow range of acceptable values for a variable.

  from typing_extensions import Literal

  def set_mode(mode: Literal['r', 'w', 'x']) -> None:
      print(f"Setting mode to {mode}")

  set_mode('r')  # Valid
  set_mode('z')  # Error
  

3. TypedDict

TypedDict allows you to define dictionaries with a specific, static structure.

  from typing_extensions import TypedDict

  class Point(TypedDict):
      x: int
      y: int

  point: Point = {'x': 10, 'y': 20}  # Valid
  

4. Protocol

Protocol makes it possible to specify structural subtyping (or “duck typing”).

  from typing_extensions import Protocol

  class DuckProtocol(Protocol):
      def quack(self) -> None:
          pass

  class Duck:
      def quack(self) -> None:
          print("Quack!")

  def use_protocol(duck: DuckProtocol) -> None:
      duck.quack()

  use_protocol(Duck())  # Valid
  

5. Self

Using Self, you can hint that an instance method returns an instance of its class.

  from typing_extensions import Self

  class Builder:
      def add_component(self, component: str) -> Self:
          print(f"Adding {component}")
          return self
  

6. NotRequired and Required

These hints can be employed with TypedDict to specify whether a key is optional or mandatory.

  from typing_extensions import TypedDict, NotRequired

  class User(TypedDict):
      name: str
      age: NotRequired[int]
  

App Example: Using Typing Extensions

Let’s build an app example utilizing the APIs introduced above to demonstrate their practical use.

  from typing_extensions import Literal, TypedDict, Protocol, final

  @final
  class Config:
      DEFAULT_MODE: Literal['r', 'w'] = 'r'

  class Settings(TypedDict):
      theme: str
      notifications: bool

  class StartupProtocol(Protocol):
      def load(self) -> None:
          pass

  class App:
      def __init__(self):
          self.settings: Settings = {'theme': 'dark', 'notifications': True}

      def start(self) -> None:
          print("Starting application...")

  app = App()
  app.start()

By combining multiple components of typing-extensions, you can create robust, error-safe, and backward-compatible Python applications with strong type hinting support.

Conclusion

The typing-extensions library is an invaluable tool for Python developers who want to adopt modern type hinting features while maintaining compatibility with older Python versions. We introduced several APIs such as @final, Literal, TypedDict, Protocol, and more, along with their usage examples. Start incorporating typing-extensions into your projects to write better Python code today!

Leave a Reply

Your email address will not be published. Required fields are marked *