Customizing positional arguments in class pattern matching

Alright ! Let’s talk about customizing positional arguments in Python class pattern matching a feature that can make managing complex classes with lots of optional parameters much easier. In this tutorial, we’ll show you how to use the `match` statement to match specific patterns of argument tuples or named parameters against your constructor signature.

First off, what is positional argument customization? Well, let’s say you have a class that takes multiple parameters when it’s instantiated (created). Normally, these arguments would be passed in the order they appear in the constructor signature:

# Defining a class called MyClass
class MyClass(object):
    # Defining a constructor method with three parameters
    def __init__(self, arg1, arg2=None, arg3=None):
        # Assigning the first argument to the instance variable arg1
        self.arg1 = arg1
        # Assigning the second argument to the instance variable arg2
        self.arg2 = arg2
        # Assigning the third argument to the instance variable arg3
        self.arg3 = arg3

But what if you want to change the order of these arguments? Or maybe you have a bunch of optional parameters that can be passed in any order, and you don’t care about their position as long as they are all present? That’s where pattern matching comes in.

In Python 3.10 (and later), we can use the `match` statement to customize how positional arguments are matched against a class constructor signature. Here’s an example:

# This is a class definition for MyClass
class MyClass(object):
    # This is the constructor method for MyClass
    def __init__(self, arg1, arg2=None, arg3=None):
        # This is a match statement that allows for customized matching of positional arguments against the class constructor signature
        match (args):
            # This case matches if the first two arguments are provided and assigns them to self.arg1 and self.arg2 respectively
            case [arg1_, arg2_]:
                self.arg1 = arg1_
                if arg2_ is not None:
                    self.arg2 = arg2_
            # This case matches if the first and third arguments are provided and assigns them to self.arg1 and self.arg3 respectively
            case [arg1_, _, arg3_]:
                self.arg1 = arg1_
                self.arg3 = arg3_
            # This case matches if no arguments are provided and raises a TypeError
            case _:
                raise TypeError("Invalid arguments")

In this example, we’ve added a `match` statement to the constructor signature. This allows us to customize how positional arguments are matched against our class constructor. The first two cases match specific patterns of argument tuples (in this case, either [arg1, arg2] or [arg1, _, arg3]). If neither pattern matches, a `TypeError` is raised with an error message.

So what’s the big deal? Well, let’s say you have a class that takes 5 arguments, but some of them are optional and can be passed in any order. With this new feature, you don’t need to worry about keeping track of which argument is which Python will handle it for you!

You can also use pattern matching with keyword arguments (i.e., named parameters) as well. Here’s an example:

# The following script is used to demonstrate the use of pattern matching with keyword arguments in Python.

# First, we define a class called MyClass.
class MyClass(object):
    # The __init__ method is used to initialize the class and its attributes.
    # It takes in two required arguments (arg1 and arg2) and two optional arguments (arg2 and arg3).
    # The asterisk (*) before arg2 indicates that it is a keyword-only argument, meaning it can only be passed in using its name.
    # This allows for more flexibility in passing arguments, as they can be passed in any order.
    def __init__(self, arg1, *, arg2=None, arg3=None):
        # We use pattern matching to handle the different possible combinations of arguments that can be passed in.
        # The first case matches when only arg1 is passed in.
        # The underscore (_) is used as a placeholder for any other arguments that may be passed in.
        match (args):
            case {arg1_}:
                # If only arg1 is passed in, we assign its value to the self.arg1 attribute.
                self.arg1 = arg1_
            # The second case matches when both arg1 and arg2 are passed in.
            # The values of arg1 and arg2 are assigned to the self.arg1 and self.arg2 attributes, respectively.
            case {'arg1': arg1_, 'arg2': arg2_}:
                self.arg1 = arg1_
                self.arg2 = arg2_
            # The third case matches when any other combination of arguments is passed in.
            # In this case, we raise a TypeError to indicate that the arguments are invalid.
            case _:
                raise TypeError("Invalid arguments")

In this example, we’ve added a `*` to the constructor signature for our optional keyword argument(s). This tells Python that these arguments can be passed in any order (as long as they are present), and will be matched against their names instead of their positions. The first two cases match specific patterns of named parameters (in this case, either {‘arg1’: arg1} or {‘arg1’: arg1, ‘arg2’: arg2}). If neither pattern matches, a `TypeError` is raised with an error message.

It’s not as scary as it sounds (or at least we tried to make it less so), and can be incredibly useful for managing complex classes with lots of optional parameters. Give it a try, and let us know what you think!

If you want more real code examples or translations, check out the PEP 622 documentation on Python’s official website (https://www.python.org/dev/peps/pep-0622/) for further details.

SICORPS