---
title: "Pattern matching in Python vs Scala"
description: "What is the difference between pattern matching in Python and Scala?"
author: "Bartosz Mikulski"
author_bio: "Principal AI Engineer & MLOps Architect. I bridge the gap between \"it works in a notebook\" and \"it works for 200 million users.\""
author_url: https://mikulskibartosz.name
author_linkedin: https://www.linkedin.com/in/mikulskibartosz/
author_github: https://github.com/mikulskibartosz
canonical_url: https://mikulskibartosz.name/pattern-matching-python-vs-scala
---

When I switched from Scala to Python, the most annoying thing was the lack of pattern matching.

In Scala, we tend to solve multiple problems using the matching operator because such notation makes it easier to work with types such as `Option`, `Either`, or `Try` when we must handle both possible values.

Thankfully, since Python 3.10, we finally have structural pattern matching, which works almost the same as the Scala implementation! Let's take a look at the similarities and differences.

## Constant matching

In both languages, we can use pattern matching to verify whether a variable has the specified value. Of course, Python doesn't have strict typing. Therefore, we can use values of different types, and the automatic type conversion sometimes gets in the way.

In Scala, I could write the following code to check the value of the `x` variable:

```scala
x match {
    case 0 => "zero"
    case true => "true"
    case "hello" => "'hello'"
    case _ => "whatever"
}
```

The equivalent Python code looks like this:

```python
match x:
    case 0: print("zero")
    case True: print("true")
    case "hello": print("'hello'")
    case _: print("whatever")
```

Beware of a trap!!! If the `x` variable has the value `False`, `0` will get matched. To solve the problem, we must verify the variable type using a guard expression. I will show the notation in one of the following examples.

## Sequence matching

Scala offers a few ways to match sequence content. For example, we can check the exact length of a sequence and bind the variables by element position:

```scala
val ints = Seq(1, 2)
ints match {
    case Seq() => "The Seq is empty!"
    case Seq(first) => s"The seq has one element : $first"
    case Seq(first, second) => s"The seq has two elements : $first, $second"
}
```

To achieve the same result in Python, we need very similar code:

```python
ints = [1, 2]

match ints:
    case []: print("The Seq is empty!")
    case [first]: print(f"The seq has one element: {first}")
    case [first, second]: print(f"The seq has two elements: {first}, {second}")
```

Of course, a common use case of retrieving the first value from a long sequence is also trivial to implement. Let's start with the Scala code:

```scala
seq match {
    case Seq(first, rest@_*) => s"First: $first, rest: $rest"
}
```

and here is the Python code:

```python
match ints:
    case [first, *rest]: print(f"First: {first}, rest: {rest}")
```

## Matching object fields

Using pattern matching to match constants and sequence elements would be pretty useless. In Scala, we can use it also to match the fields (and their values) of case classes.

```scala
case class Person(firstName: String, lastName: String)

author = Person("Bartosz", "Mikulski")

author match {
    case Person("Bartosz", last_name) => s"$last_name"
}
```

To get something similar to a Scala case class in Python, we have to use dataclasses and define the following class:

```python
from dataclasses import dataclass

@dataclass
class Person:
    first_name: str
    last_name: str
```

Now, we can instantiate it and try matching the content:

```python
author = Person("Bartosz", "Mikulski")

match author:
    case Person("Bartosz", last_name): print(f"Hi, Bartosz {last_name}")
```

## Matching object fields without dataclasses

What if we had a standard Python class instead of a dataclass? Would it work?

```python
class NotADataClass:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

another_author = NotADataClass("Bartosz", "Mikulski")

match another_author:
    case NotADataClass("Bartosz", _): print("Hi, Bartosz")
```

Unfortunately, such notation isn't supported, and when we try running the code, we will get an error: `TypeError: NotADataClass() accepts 0 positional sub-patterns (2 given)`.

However, if we can modify the class definition by adding a special `__match_args__` field to tell Python which arguments to use during pattern matching:

```python
class NotADataClass:
    __match_args__ = ("first_name", "last_name")
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

another_author = NotADataClass("Bartosz", "Mikulski")

match another_author:
    case NotADataClass("Bartosz", _): print("Hi, Bartosz")
```

Of course, if we want to match a class imported from someone else's library, the only thing we can do is wait until they include the `__match_args__` in their code, or we can try patching the class definition:

```python
NotADataClass.__match_args__ = ("first_name", "last_name")
```

It will work too. Nevertheless, such patching is quite error-prone, and I don't recommend doing it.

## Pattern guards

Similarly to Scala, we can use the `if` statement in the pattern definition to limit the matching:

```python
match another_author:
    case NotADataClass(first_name, _) if first_name == "Bartosz": print("Hi, Bartosz")
```

The condition looks precisely like its Scala equivalent, so let's move on ;)

## Mapping patterns

Now, let's take a look at something that doesn't exist in Scala (or at least it didn't exist in the last version I was using). We can use pattern matching in Python to check whether a dictionary contains a specified key and value.

```python
dict_matching = {
    "first_name": "Bartosz",
    "last_name": "Mikulski"
}

match dict_matching:
    case {"first_name": "Bartosz"}: print("Hi, Bartosz")
```

We can also bind the values to a variable, but it isn't allowed to bind the keys. The key must always be literal.

```python
match dict_matching:
    case {"first_name": "Bartosz", "last_name": last_name}: print(f"Hi, Bartosz {last_name}")
```

## Matching alternatives

In Scala, we can use pattern alternatives too. The most common usecase is to match multiple exception classes:

```scala
case _: RuntimeException | _: IOException => ""
```

Python gives us a similar pattern alternatives notation:

```python
match dict_matching:
    case {"first_name": ""} | {"first_name": "Bartosz"}: print("HI!")
```

## No matching found

The most shocking difference between Python and Scala is how the pattern matching handles match statements that didn't match anything.

In Scala, the following match expression throws a `scala.MatchError` error:

```scala
val x = 0
x match {
    case 1 => "one"
    case 2 => "two"
}
```

The equivalent Python expression will... do nothing. In Python, pattern matching silently ignores situations when it fails to match anything.

## The hardest thing to remember

As we see, the Python notation is almost identical to Scala pattern matching. For me, the biggest problem is remembering to write `match` BEFORE the variable name. After all, in Scala, we do it the other way around.