Structural Pattern Matching in Python III

In this final blog of the series, we conclude our discussion with the introduction of Class Pattern.

GraphQL has a role beyond API Query Language- being the backbone of application Integration
background Coditation

Structural Pattern Matching in Python III

In our first blog, we discussed the structured pattern-matching feature in Python 3.10 where we explain literal patterns, capture patterns, wildcard patterns, AS patterns, OR patterns, and guard patterns.

In the second blog, we discussed value patterns, sequence patterns, and mapping patterns.

In this blog we talk about class pattern matching.

Class Pattern matching

Matching a class pattern with no arguments depends on if an instance call is a success or not.

Let us understand with an example:


# Matching to a class pattern without arguments

class Furniture:
    
    def __init__(self, name: str, price: float):
        self.name = name
        self.price = price

subject = Furniture("table", 2200)

match subject:
    case Furniture():
        print(f"This {subject.name} will cost around {subject.price}")

If a python class without class attribute __match_args__ is used as a class pattern and has arguments in the class pattern, Will result in TypeError.


# Matching a class pattern with argument where python class doesn't have a `__match_args__` class attribute (Will result in Error)

subject = Furniture("table", 2200)  # Output: TypeError: Furniture() accepts 0 positional sub-patterns (2 given)

match subject:
    case Furniture("table", 2200):
        print(f"This {subject.name} will cost around {subject.price}")


# Matching a class pattern with argument where python class have a `__match_args__` class attribute

class Furniture:

    __match_args__ = ("price", "name")

    def __init__(self, name: str, price: float):
        self.name = name
        self.price = price

subject = Furniture("table", 2200) # Output: The cost of this table is 2200

match subject:
    case Furniture("table", 2200):
        print(f"This {subject.name} will cost around {subject.price}")
    case Furniture(2200, "table"):
        print(f"The cost of this {subject.name} is {subject.price}")

In the above example, even though the subject Furniture("table", 2200) and pattern for 1st case Furniture("table", 2200) are the same it’s not a match as the order of positional argument doesn’t match with __match_args__ attribute, while it matches with 2nd case Furniture(2200, "table")

To make things easier w.r.t order of argument or the __match_args__ attribute, data classes, or named tuples can be used as these classes will already have this attribute.


# Matching a class pattern with argument for a dataclass type

from dataclasses import dataclass

@dataclass
class Student:
    name: str
    marks: tuple

subject = Student("Ramesh", (98, 79)) # Output: Ramesh scored 79 in Science

match subject:
    case Student((maths, _), name):
        print(f"{name} scored {maths} in mathematics")
    case Student(name, (_, science)):
        print(f"{name} scored {science} in Science")

For a data class, the order of __match_args__ is the same as to order in its constructor(Or while declaring a data class). In a data class if the init field param of an attribute is False, then this attribute will not be included in the __match_args__ tuple.

For some built-in types the positional arguments/sub patterns are not matched as we have seen earlier, These types include: primitive data types like: str. int, float, and bool collection data types like list, tuple, set, and dict Also bytes, frozen set, byte array

When a class pattern is declared using these above built-in types only one argument I allowed for the pattern, which then gets assigned a value of the subject if the subject matches the pattern.


# Matching a class pattern with argument for a builtin types

subject = 56 # Output: 56 is an integer
subject = "Hello World" # Output: Hello World is a string
subject = (22,) # Output: We got a tuple of length 1

match subject:
    case int(interger):
        print(f"{interger} is an integer")
    case str(string):
        print(f"{string} is a string")
    case tuple(tuple_):
        print(f"We got a tuple of length {len(tuple_)}")

If more than 1 argument/sub pattern is passed to such builtin types it will result in a TypeError

Conclusion

In this 3 part series, we introduced the structured python matching feature introduced in Python 3.10. While SPM is generally considered a feature of functional and hybrid programming languages, Python embracing SPM opens up new possibilities for structuring the codebase. However, it remains to be seen how much of this feature is adopted by the Python community.

Hi, I am Harris Hujare. As a software developer, I design and implement scalable and maintainable systems. I have a passion for learning new technologies and creating software utilities making lives easier.

Want to receive update about our upcoming podcast?

Thanks for joining our newsletter.
Oops! Something went wrong.

Latest Articles

Designing multi-agent systems using LangGraph for collaborative problem-solving

Learn how to build sophisticated multi-agent systems using LangGraph for collaborative problem-solving. This comprehensive guide covers the implementation of a software development team of AI agents, including task breakdown, code implementation, and review processes. Discover practical patterns for state management, agent communication, error handling, and system monitoring. With real-world examples and code implementations, you'll understand how to orchestrate multiple AI agents to tackle complex problems effectively. Perfect for developers looking to create robust, production-grade multi-agent systems that can handle iterative development workflows and maintain reliable state management.

time
7
 min read

Designing event-driven microservices architectures using Apache Kafka and Kafka Streams

Dive into the world of event-driven microservices architecture with Apache Kafka and Kafka Streams. This comprehensive guide explores core concepts, implementation patterns, and best practices for building scalable distributed systems. Learn how to design event schemas, process streams effectively, and handle failures gracefully. With practical Java code examples and real-world architectural patterns, discover how companies like Netflix and LinkedIn process billions of events daily. Whether you're new to event-driven architecture or looking to optimize your existing system, this guide provides valuable insights into building robust, loosely coupled microservices.

time
12
 min read

Implementing Custom Instrumentation for Application Performance Monitoring (APM) Using OpenTelemetry

Application Performance Monitoring (APM) has become crucial for businesses to ensure optimal software performance and user experience. As applications grow more complex and distributed, the need for comprehensive monitoring solutions has never been greater. OpenTelemetry has emerged as a powerful, vendor-neutral framework for instrumenting, generating, collecting, and exporting telemetry data. This article explores how to implement custom instrumentation using OpenTelemetry for effective APM.

Mobile Engineering
time
5
 min read