---
title: "Python Fuzzy Search mit Rust & PyO3"
description: "Fuzzy Search in Python mit Rust & PyO3 integrieren. Leistungsstark und effizient mit Maturin und Nucleo. Jetzt mehr erfahren!"
canonical_url: "https://www.blueshoe.de/blog/python-rust-pyo3/"
---

Fuzzy Search findet unscharfe Übereinstimmungen in Strings, stößt in Python aber bei großen Datenmengen an Grenzen. Rust löst dieses Problem effizient. Mit PyO3 lässt sich Rust als Python-Erweiterung nutzen, um rechenintensive Prozesse zu beschleunigen. Dieser Artikel zeigt, wie Fuzzy Search in Python mit Rust umgesetzt wird und welche Werkzeuge – insbesondere PyO3, Maturin und die Nucleo-Bibliothek – dabei helfen.

![Effizient Python erweitern: PyO3 und Rust im Einsatz](/img/blogs/python-rust-pyo3.png)

<global-blog-level-info>

- [Rust Programmiersprache](https://rust-lang.org/)
- Optional [PyO3](https://github.com/PyO3/pyo3)
- Optional [uv](https://github.com/astral-sh/uv)

Solltest du Fragen haben, oder dir etwas unklar sein, kannst du die Kommentarfunktion unter dem Artikel nutzen.

</global-blog-level-info>

<global-title :size="lg" className="mb-4">

Pfuzzer: Ein Python Fuzzy Searcher mit Rust

</global-title>

<global-paragraph>

Die Kombination von **Rust** und **Python** wird immer beliebter, insbesondere für performance-sensitive Anwendungen. Mit [**PyO3**](https://github.com/PyO3/pyo3) lassen sich Rust-Module als Python-Erweiterung nutzen, wodurch sich die Geschwindigkeit und Effizienz von Rust mit der Flexibilität von Python verbinden lassen.

</global-paragraph>

<global-paragraph>

Ein herausragendes Beispiel für diese Integration ist **Pfuzzer** – eine Python-Bibliothek für Fuzzy Search, die auf der leistungsstarken Rust-Bibliothek [**Nucleo**](https://github.com/helix-editor/nucleo) basiert. Doch Pfuzzer ist mehr als nur eine praktische Lösung für unscharfe Suchanfragen: Es zeigt, wie **PyO3** genutzt werden kann, um Rust-Code nahtlos in Python einzubinden und rechenintensive Prozesse zu optimieren.

</global-paragraph>

<global-paragraph>

In diesem Artikel erfährst du, wie Pfuzzer funktioniert, wie PyO3 die Entwicklung von Rust-Erweiterungen für Python erleichtert und wie du eine Fuzzy Search in Python mit Rust umsetzen kannst.

</global-paragraph>

<global-button :color="blue" :label="Erfahre mehr über unsere Python-Entwicklungsdienste" :url="/technologien/python-django-agentur/" className="mb-6">



</global-button>

<global-title :size="lg" className="mb-5">

Was ist PyO3?

</global-title>

<global-paragraph>

PyO3 ist ein Framework, das Rust-Code als native Python-Module verfügbar macht. Damit lassen sich Rust-Programme in Python integrieren, ohne auf Leistung oder Speichersicherheit verzichten zu müssen. Mit PyO3 kannst du:

</global-paragraph>

<global-block className="ul-disk,my-4">

- Python-APIs mit Rust entwickeln
- Bestehende Rust-Bibliotheken in Python einbinden
- Python-Funktionen aus Rust heraus aufrufen

</global-block>

<global-paragraph>

Das Framework übernimmt technische Details wie Speicherverwaltung, Datenstrukturen und die Interaktion zwischen Python- und Rust-Laufzeit.

</global-paragraph>

<global-paragraph>

Dank der hohen Performance von Rust und der Flexibilität von Python ist PyO3 ideal für rechenintensive Anwendungen, darunter Datenverarbeitung, maschinelles Lernen und performante Fuzzy-Suchfunktionen – wie es Pfuzzer demonstriert.

</global-paragraph>

<global-title :size="lg" className="mb-5">

Pfuzzer: Ein Python-Modul für Fuzzy Search mit Rust

</global-title>

<global-paragraph>

Pfuzzer ist eine Python-Bibliothek für Fuzzy Search, die auf der Rust-Bibliothek Nucleo basiert. Sie ermöglicht eine schnelle und effiziente unscharfe Suche – ideal für Anwendungen mit großen Datenmengen.

</global-paragraph>

<global-title :size="md" className="mb-5" :font="font-oswald" :tag="h3" :color="text-bs-blue">

Warum Rust für Fuzzy Search?

</global-title>

<global-block className="ul-disk,my-4">

- **Performance**: Rust ist deutlich schneller als reines Python
- **Speichersicherheit**: Kein Garbage Collector, kein Memory Leak
- **Einfache Integration**: Mit PyO3 als Python-Erweiterung in Rust nutzbar

</global-block>

<global-title :size="lg" className="mb-5">

Pfuzzer installieren und einrichten

</global-title>

<global-paragraph>

Um ein Rust-Projekt mit PyO3 in Python zu nutzen, eignet sich [**Maturin**](https://github.com/PyO3/maturin). Maturin erleichtert den Build-Prozess, indem es Rust-Code als Python-Wheel kompiliert. So kann das Modul wie jede andere Python-Bibliothek installiert und verwendet werden.

</global-paragraph>

<global-title :size="md" className="mb-5" :font="font-oswald" :tag="h3" :color="text-bs-blue">

Einrichtung eines PyO3-Projekts

</global-title>

<global-title :size="s" className="mb-2" :font="font-oswald" :tag="h4">

1. Projekt erstellen

</global-title>

<global-paragraph>

Für die Projektstruktur nutzen wir den uv Package Manager::

</global-paragraph>

<blog-code className="mb-4">

```bash
$ mkdir pfuzzer
$ cd pfuzzer
$ uv venv
$ uv add maturin
$ . .env/bin/activate
```

</blog-code>

<global-title :size="s" className="mb-2" :font="font-oswald" :tag="h4">

2. Maturin initialisieren

</global-title>

<global-paragraph>

Führe Maturin aus und lasse dir alle benötigten Dateien erstellen:

</global-paragraph>

<blog-code className="mb-4">

```bash
$ maturin init
✔  🤷 What kind of bindings to use? · pyo3
   ✨ Done! New project created pfuzzer
```

</blog-code>

<global-paragraph>

Dieser Befehl erzeugt die Dateien `Cargo.toml` (Rust-Dependency-Management) und `lib.rs` (Rust-Code, der aus Python aufgerufen wird).

</global-paragraph>

<global-paragraph>

Der Befehl `maturin develop` installiert dann dein frisch erstelltes Rust Package in dem vorher erstellten virtualenv. Da wir uv benutzen muss die `--uv` flag ergänzt werden. Mit `maturin build –release` kann das Package für den Roll-Out gebuildet werden.

</global-paragraph>

<global-title :size="lg" className="mb-5">

Pfuzzer: Implementierung und Funktionsweise

</global-title>

<global-paragraph>

Das Pfuzzer Python Package ist ein Wrapper für die Rust Library nucleo. Wie bereits gezeigt ist ein Wrapper einer bestehenden Library leicht mit PyO3 umzusetzen. Trotzdem will ich kurz auf die Implementierung eingehen und die Funktionsweise erklären. Der [gesamte Code kann hier eingesehen](https://github.com/Blueshoe/pfuzzer) werden.

</global-paragraph>

<global-paragraph>

Dank PyO3 ist die Implementierung recht einfach. So stellt es mehrere Rust Attributes bereit, um Rust Code in Python aufrufbar zu machen. Hier z. B. die Implementierung des grundsätzlichen Python Moduls:

</global-paragraph>

<blog-code className="mb-4">

```rust
mod python_classes;
use pyo3::prelude::*;
/// A Python fuzzy searcher module implemented in Rust.
#[pymodule]
fn pfuzzer(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_class::<python_classes::pfuzzer::Pfuzzer>()?;
    Ok(())
}
```

</blog-code>

<global-paragraph>

Das ist so wenig Code, man könnte fast meinen, es ist bereits in Python geschrieben. Aber kurz die wichtigsten Abschnitte erklärt:

</global-paragraph>

<global-block className="ol-decimal,my-4">

1. `#[pymodule]` --> teilt dem compiler mit, dass es sich hier um ein Python Modul handelt. Dadurch wird in die Funktion PyModule als Variable injected, welche dann als Eingangspunkt für unser Python Modul benutzt werden kann.
2. `m.add_class::<...>` --> weist unserem Modul eine Klasse zu, in diesem Fall die noch nicht spezifizierte Pfuzzer Klasse.

</global-block>

<global-call-an-expert :bg="bg-bs-blue">
<template v-slot:buttonBlockTitle="">

Wir können auch deine Python Anwendung mit Rust und PyO3 beschleunigen.

</template>
</global-call-an-expert>

<global-paragraph>

Es gibt noch andere Möglichkeiten Rust Code dem Modul zuzuweisen. Darunter `m.add_function`. Anders als bei `add_class` wird hier allerdings nicht die Turbofish syntax verwendet sondern man muss die Rust Function mit einem Makro aufrufen. Mehr dazu in [den PyO3 docs](https://pyo3.rs).

</global-paragraph>

<global-title :size="lg" className="mb-5">

Pfuzzer-Klasse in Rust umsetzen

</global-title>

<global-paragraph>

Da Rust keine Klassen kennt, verwenden wir stattdessen Structs, um das Verhalten der Pfuzzer-Klasse zu definieren:

</global-paragraph>

<blog-code className="mb-4">

```rust
#[pyclass]
pub struct Pfuzzer {
    pub matcher: Matcher,
}
```

</blog-code>

<global-paragraph>

Als Attribut wird hier `pyclass` vergeben. Dadurch wird das Struct als Python Klasse markiert und kann dann, wie vorher gezeigt, dem Modul zugewiesen werden.

</global-paragraph>

<global-title :size="lg" className="mb-5">

Konstruktor für Pfuzzer

</global-title>

<global-paragraph>

Jede gute Klasse braucht natürlich auch einen Konstruktor:

</global-paragraph>

<blog-code className="mb-4">

```rust
use nucleo::{Config, Matcher, Utf32Str};

#[pymethods]
impl Pfuzzer {
    #[new]
    pub fn new() -> PyResult<Self> {
        Ok(Pfuzzer {
            matcher: Matcher::new(Config::DEFAULT),
        })
    }

...
```

</blog-code>

<global-paragraph>

Durch `#[pymethods]` deklarieren wir den gesamten Implementation Block als Python Methoden der `Pfuzzer` Klasse. Der Konstruktor selbst wird mit `#[new]` markiert. Aktuell unterstützt PyO3 nämlich nur die `__new__` Magic Method und nicht `__init__`.

</global-paragraph>

<global-title :size="lg" className="mb-5">

Fuzzy Search in Rust: `compare_strings` Methode

</global-title>

<global-paragraph>

Zu guter Letzt noch die eigentliche “Logik” unseres Wrappers. Ich darf präsentieren, die `compare_strings` Methode:

</global-paragraph>

<blog-code className="mb-4">

```rust
pub fn compare_strings(&self, targets: Vec<String>, query: String) -> Vec<Option<u16>> {
        let mut res = Vec::<Option<u16>>::new();
        for target in targets {
            res.push(self.matcher.to_owned().fuzzy_match(
                Utf32Str::Ascii(target.as_bytes()),
                Utf32Str::Ascii(query.as_bytes()),
            ))
        }
        return res;
    }
```

</blog-code>

<global-paragraph>

Wie man schnell erkennen kann, benutzt sie den nucleo Matcher und führt für jeden Ziel String das fuzzy matching anhand des angegebenen Query Strings aus.<br />


Ergebnis der Methode ist dann ein optionaler Integer pro Ziel String, welcher aussagt, wie gut der Query String mit dem Ziel String übereinstimmt. Je höher das Ergebnis, desto besser die Übereinstimmung. Ein *Null* / *None* Wert sagt aus, dass Ziel und Query überhaupt nicht (oder zumindest nicht messbar) übereinstimmen.

</global-paragraph>

<global-title :size="lg" className="mb-5">

Pfuzzer in Python verwenden

</global-title>

<global-paragraph>

Und wie sieht das Ganze nun in Python aus? Bitteschön:

</global-paragraph>

<blog-code className="mb-4">

```python
from pfuzzer import Pfuzzer

pf = Pfuzzer()

print(pf.compare_strings(["hello world", "hello blueshoe"], "helo world"))

>>> [257, None]
```

</blog-code>

<global-paragraph>

Laut Ergebnis Matrix, ist also der erste String der String, welcher am besten mit der Query übereinstimmt.

</global-paragraph>

<global-paragraph>

Wenn du noch Ideen für neue Features für Pfuzzer, oder gar Optimierungsgedanken, hast dann lass gerne einen Kommentar da. Oder mach ein [Issue auf Github](https://github.com/Blueshoe/pfuzzer/issues) auf!

</global-paragraph>

<global-title :size="lg" className="mb-5">

Fazit: Rust und Python effizient kombiniert

</global-title>

<global-paragraph>

In diesem Artikel haben wir gezeigt, wie Rust und Python mit PyO3 zusammenarbeiten, um performante Lösungen für Fuzzy Search zu ermöglichen. Pfuzzer ist ein Beispiel dafür, wie sich die Geschwindigkeit und Sicherheit von Rust mit der Flexibilität von Python verbinden lassen. Die nahtlose Integration über PyO3 und Maturin eröffnet neue Möglichkeiten – besonders für rechenintensive Anwendungen wie Datenverarbeitung und maschinelles Lernen.

</global-paragraph>

<global-paragraph>

Wenn auch du Lust auf richtig schnelles Python hast, dann schreib uns gerne in den Kommentaren und wir erörtern die Einsatzmöglichkeiten von Rust in deinem Python Projekt! Auch über Kommentare zu deinem Einsatz von PyO3 freuen wir uns!

</global-paragraph>

<global-title :size="lg" className="mb-5,mt-8">

Häufige Fragen

</global-title>

<global-title :size="md" className="mb-5" :font="font-oswald" :tag="h3" :color="text-bs-blue">

1. Wie kann ich ein Python-Modul mit Rust und PyO3 erstellen?

</global-title>

<global-paragraph>

Um ein Rust-Modul für Python zu schreiben, brauchst du PyO3 und Maturin. Die grundlegenden Schritte:

</global-paragraph>

<global-block className="ol-decimal,my-4">

1. **Rust-Umgebung einrichten:** Folgende Kommandos: `cargo new --lib my_project`, `cd my_project`.
2. **PyO3 hinzufügen:** Füge in der `Cargo.toml` die Dependency `pyo3 = { version = "0.18", features = ["extension-module"] }` hinzu.
3. **Modul implementieren:** `use pyo3::prelude::*;` und `#[pymodule]`
4. **Mit Maturin bauen:** `maturin build`

</global-block>

<global-title :size="md" className="mb-5" :font="font-oswald" :tag="h3" :color="text-bs-blue">

2. Wie funktioniert Fuzzy Search in Rust mit der Nucleo-Bibliothek?

</global-title>

<global-paragraph>

Die Nucleo-Bibliothek bietet einen leistungsstarken Fuzzy Matching Algorithmus, um die Ähnlichkeit zwischen Strings zu berechnen. *Pfuzzer* nutzt diese Bibliothek für schnelle unscharfe Suchen in Python.

</global-paragraph>

<global-title :size="md" className="mb-5" :font="font-oswald" :tag="h3" :color="text-bs-blue">

3. Kann ich Rust-Module in bestehenden Python-Projekten verwenden?

</global-title>

<global-paragraph>

Ja! Rust-Module lassen sich problemlos in bestehende Python-Projekte integrieren. Dank **PyO3** kannst du Rust-Funktionen direkt als Python-Module importieren und nutzen.

</global-paragraph>

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
