5 weitere unverzichtbare Python-Konzepte

# Einführung

Python frisst die Welt. Seit seiner Einführung vor über 35 Jahren hat sich Python erfolgreich in die Herzen von Programmierern auf der ganzen Welt gedrängt. Python ist eine leistungsstarke Allzweck-Programmiersprache mit einer einfachen Syntax, einer umfassenden Benutzergemeinschaft und einer Vielzahl unterstützender Bibliotheken in ihrem Ökosystem. Dies hat dazu beigetragen, es zu einer der bevorzugten Sprachen für Datenwissenschaft, maschinelles Lernen und KI zu machen. Darüber hinaus ist Python einfach um damit anzufangen (relativ gesehen). Lassen Sie sich jedoch nicht täuschen; Sie können noch Jahre damit verbringen, Ihre Fähigkeiten zu verbessern und die Kernmechanismen der Sprache zu beherrschen. Deshalb sind wir heute hier.

In einem früheren Artikelhaben wir unsere ersten fünf unverzichtbaren Python-Konzepte behandelt: Listenverständnisse und Generatorausdrücke; Dekorateure; Kontextmanager (with Aussagen); beherrschen *args Und **kwargs; und Dunder-Methoden (magische Methoden). Werfen wir nun einen Blick auf fünf weitere grundlegende Konzepte, die jeder Python-Entwickler in seinem Toolkit haben sollte.

# 1. Geben Sie Hinting & MyPy ein

Python ist dynamisch typisiert, was bedeutet, dass es nicht notwendig ist, Variablentypen zu deklarieren. Dies erleichtert zwar das Fast Prototyping erheblich, kann jedoch mit der Skalierung Ihrer Codebasis zu einem Wartungsalbtraum werden. Ohne Typsicherheit kann ein einfacher Tippfehler oder ein nicht übereinstimmender Rückgabewert zu Laufzeitabstürzen in der Produktion führen. Die Lösung ist die von Python Schreibmodulmit dem Sie Ihren Code mit Anmerkungen versehen können, und MyPyein statischer Typprüfer, der Ihre Codebasis vor der Ausführung auf Fehler durchsucht.

// Der klobige Weg

Schauen wir uns eine typische, untypisierte Python-Funktion an, bei der wir die erwarteten Typen erraten müssen:

def process_user_profile(user_info):
    # What keys are inside user_info? Is age an int or a string?
    identify = user_info.get("identify", "Visitor")
    age = user_info.get("age", 0)
    tags = user_info.get("tags", ())
    
    # Susceptible to runtime error if tags is just not an iterable of strings
    return f"{identify} is {age} years outdated and tagged with: {', '.be a part of(tags)}"

# A runtime crash ready to occur if we go numbers within the tags record
print(process_user_profile({"identify": "Alice", "age": "twenty", "tags": (1, 2)}))

Ausgabe:

Traceback (most up-to-date name final):
  File "./testing.py", line 11, in 
    print(process_user_profile({"identify": "Alice", "age": "twenty", "tags": (1, 2)}))
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "./testing.py", line 8, in process_user_profile
    return f"{identify} is {age} years outdated and tagged with: {', '.be a part of(tags)}"
                                                         ^^^^^^^^^^^^^^^
TypeError: sequence merchandise 0: anticipated str occasion, int discovered

// Der pythonische Weg

Werfen wir nun einen Blick auf die Pythonic-Methode mit expliziten Typanmerkungen und einem strukturierten Schema:

from typing import TypedDict

class UserProfile(TypedDict):
    identify: str
    age: int
    tags: record(str)

def process_user_profile(user_info: UserProfile) -> str:
    identify = user_info.get("identify", "Visitor")
    age = user_info.get("age", 0)
    tags = user_info.get("tags", ())
    return f"{identify} is {age} years outdated and tagged with: {', '.be a part of(tags)}"

# Right name matching the TypedDict schema
print(process_user_profile({"identify": "Alice", "age": 28, "tags": ("Pythonist", "Engineer")}))

# Unhealthy name that shall be caught by static evaluation
process_user_profile({"identify": "Bob", "age": "thirty", "tags": (10, 20)})

Ausgabe beim Ausführen der statischen MyPy-Analyse über mypy <script_name.py>:

testing.py:18: error: Incompatible sorts (expression has kind "str", TypedDict merchandise "age" has kind "int")  (typeddict-item)
testing.py:18: error: Listing merchandise 0 has incompatible kind "int"; anticipated "str"  (list-item)
testing.py:18: error: Listing merchandise 1 has incompatible kind "int"; anticipated "str"  (list-item)
Discovered 3 errors in 1 file (checked 1 supply file)

Durch die Verwendung von Typanmerkungen wird Ihr Code selbstdokumentierend, sodass IDEs eine einwandfreie automatische Vervollständigung ermöglichen und Fehler sofort hervorheben können. Durch die Integration von MyPy in Ihre CI/CD-Pipeline wird sichergestellt, dass Typkonflikte blockiert werden, bevor Ihr Code auch nur annähernd in die Produktion gelangt.

# 2. Funktionale Programmiertools

Während Python in erster Linie objektorientiert ist, verfügt es über starke funktionale Programmierfähigkeiten. Beherrschung von Werkzeugen wie map(), filter()und die Standardbibliotheken itertools-Modul ermöglicht Ihnen die elegante, hocheffiziente Bearbeitung großer Datenmengen und mit minimalem Speicherverbrauch.

// Der klobige Weg

Nehmen wir an, wir haben Transaktionsdaten und möchten diese sortieren, nach Abteilung gruppieren und die Transaktionswerte für jede Abteilung summieren. Die Verwendung grundlegender Schleifen erfordert viel manuelle Wörterbuchverwaltung:

transactions = (
    {"dept": "IT", "quantity": 100},
    {"dept": "HR", "quantity": 50},
    {"dept": "IT", "quantity": 200},
    {"dept": "HR", "quantity": 150},
)

# Guide grouping and summing
grouped_data = {}

for t in transactions:
    dept = t("dept")
    if dept not in grouped_data:
        grouped_data(dept) = 0
    grouped_data(dept) += t("quantity")

print(grouped_data)

// Der pythonische Weg

Mithilfe funktionaler Instruments können wir Gesamtwerte in einer sauberen Pipeline sortieren, gruppieren und berechnen. Wir werden auch verwenden itertools.chain So reduzieren Sie verschachtelte Iterables ohne Kopieraufwand:

from itertools import groupby, chain
from operator import itemgetter

transactions = (
    {"dept": "IT", "quantity": 100},
    {"dept": "HR", "quantity": 50},
    {"dept": "IT", "quantity": 200},
    {"dept": "HR", "quantity": 150},
)

# groupby requires the record to be pre-sorted by the grouping key
sorted_tx = sorted(transactions, key=itemgetter("dept"))

# Group and sum elegantly in a single comprehension
department_totals = {
    dept: sum(t("quantity") for t in group)
    for dept, group in groupby(sorted_tx, key=itemgetter("dept"))
}

print(department_totals)

# The "must-know" twist: Flattening lists immediately with itertools.chain
nested_ids = ((101, 102), (201, 202), (301))
flat_ids = record(chain.from_iterable(nested_ids))

print(f"Flattened: {flat_ids}")

Ausgabe:

{'HR': 200, 'IT': 300}
{'HR': 200, 'IT': 300}
Flattened: (101, 102, 201, 202, 301)

Funktionale Pipelines sind nicht nur sauberer, sondern oft auch schneller, da die Iteration auf hochoptimierte C-Degree-Interna übertragen wird. Darüber hinaus sind Instruments wie chain Verarbeiten Sie Elemente träge und halten Sie so den Speicheraufwand gering.

# 3. Klassen und Vererbung

Python unterstützt die Mehrfachvererbung, sodass eine Klasse von mehreren übergeordneten Klassen erben kann. Dies führt jedoch den Klassiker ein Diamantproblemwobei Python herausfinden muss, welche Methode der übergeordneten Klasse zuerst ausgeführt werden soll. Um diese kooperative Vererbung zu verwalten, verwendet Python einen Algorithmus namens C3-Linearisierung um die zu berechnen Methodenauflösungsreihenfolge (MRO).

// Der klobige Weg

Das Aufrufen von Basiskonstruktoren durch expliziten Verweis auf die Namen der übergeordneten Klassen unterbricht die kooperative Vererbungskette und führt dazu, dass Basisklassen mehrmals initialisiert werden:

class Base:
    def __init__(self):
        print("Base Init")

class A(Base):
    def __init__(self):
        Base.__init__(self)
        print("A Init")

class B(Base):
    def __init__(self):
        Base.__init__(self)
        print("B Init")

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print("C Init")

# Base init will run twice
c = C()

Ausgabe:

Base Init
A Init
Base Init
B Init
C Init

// Der pythonische Weg

Nutzung der kooperativen Vererbung mit tremendous() stellt sicher, dass jeder Konstruktor in der Vererbungskette genau einmal aufgerufen wird, wobei die berechnete MRO-Liste berücksichtigt wird:

class Base:
    def __init__(self):
        print("Base Init")

class A(Base):
    def __init__(self):
        tremendous().__init__()
        print("A Init")

class B(Base):
    def __init__(self):
        tremendous().__init__()
        print("B Init")

class C(A, B):
    def __init__(self):
        tremendous().__init__()
        print("C Init")

# Base Init runs precisely as soon as
c = C()

# Inspecting the Technique Decision Order (MRO)
print("nMethod Decision Order:")

for cls in C.__mro__:
    print(f" -> {cls}")

Ausgabe:

Base Init
B Init
A Init
C Init

Technique Decision Order:
 -> <class '__main__.C'>
 -> <class '__main__.A'>
 -> <class '__main__.B'>
 -> <class '__main__.Base'>
 -> <class 'object'>

Beachten Sie, dass bei der kooperativen Vererbung tremendous().__init__() innen A ruft tatsächlich den Konstruktor von auf Bnicht Base. Das liegt daran tremendous() sucht im berechneten MRO nach der nächsten Klasse und macht die dynamische Mehrfachvererbung vorhersehbar und sturdy.

# 4. Strukturmusteranpassung

Seit Jahren verlassen sich Python-Entwickler auf umfangreiche if-elif-else Blöcke zum Weiterleiten von Logik basierend auf Datenformen. Dies funktioniert zwar, führt jedoch zu ausführlichem, schwer zu wartendem Code, wenn es um komplexe verschachtelte Strukturen wie JSON-Nutzlasten oder geparste Syntaxbäume geht. Python 3.10 eingeführt Strukturmustervergleich über match/case. Es handelt sich bei weitem nicht um eine einfache Change-Anweisung, sondern um ein leistungsstarkes Dekonstruktionstool, das sowohl die Werte als auch die Type Ihrer Daten anpasst.

// Der klobige Weg

Angenommen, wir verarbeiten eingehende API-Ereignisnachrichten. Wir müssen ihren Typ analysieren, ihre Struktur überprüfen und innere Werte extrahieren:

def handle_event(occasion):
    if not isinstance(occasion, dict):
        return "Invalid occasion format"
    
    event_type = occasion.get("kind")
    
    if event_type == "login":
        consumer = occasion.get("consumer")
        if consumer:
            return f"Person {consumer} logged in"

    elif event_type == "fee":
        quantity = occasion.get("quantity")
        forex = occasion.get("forex", "USD")
        if isinstance(quantity, (int, float)):
            return f"Fee of {quantity} {forex} processed"

    elif event_type == "logout":
        return "Person logged out"
        
    return "Unknown or malformed occasion"

// Der pythonische Weg

Hier ist der elegante, deklarative Ansatz mit match Und case um Muster abzugleichen und verschachtelte Variablen in einem Schritt zu extrahieren:

def handle_event(occasion: dict) -> str:
    match occasion:
        case {"kind": "login", "consumer": str(consumer)}:
            return f"Person {consumer} logged in"
            
        case  float(amt), "forex": str(curr):
            return f"Fee of {amt} {curr} processed"
            
        case  float(amt):
            # Fallback for fee if forex is lacking (defaulting to USD)
            return f"Fee of {amt} USD processed"
            
        case {"kind": "logout"}:
            return "Person logged out"
            
        case _:
            return "Unknown or malformed occasion"

print(handle_event({"kind": "fee", "quantity": 250, "forex": "EUR"}))
print(handle_event({"kind": "login", "consumer": "Alice"}))
print(handle_event({"kind": "fee", "quantity": "invalid"}))

Ausgabe:

Fee of 250 EUR processed
Person Alice logged in
Unknown or malformed occasion

Struktureller Mustervergleich bindet Variablen (wie consumer oder amt) nur dann im laufenden Betrieb, wenn das Muster erfolgreich übereinstimmt, wodurch die Boilerplate-Extraktion und die Validierungslogik entfallen. Dies ist besonders nützlich beim Erstellen von Compilern, Zustandsmaschinen und komplexen Datenaufnahmepipelines.

# 5. Virtuelle Umgebungen und Abhängigkeitsmanagement

Jeder Python-Entwickler beginnt mit der globalen Set up von Paketen mithilfe von pip set up package_name. Im Laufe der Zeit erfordern verschiedene Projekte widersprüchliche Versionen von Bibliotheken, was zur Abhängigkeitshölle führt. Während standardmäßige virtuelle Umgebungen und grundlegende necessities.txt Dateien bieten eine rudimentäre Isolation, es fehlen ihnen Sperrdateien, um sicherzustellen, dass die transitiven (Unter-)Abhängigkeiten in allen Umgebungen vollständig deterministisch sind. Um robuste, reproduzierbare Systeme aufzubauen, sollten Sie auf moderne Verwaltungstools wie migrieren Poesie oder Conda.

// Der moderne Anwendungsstandard (Poesie)

Poesie Konsolidiert Konfiguration, Paketierung und Abhängigkeiten in einem einzigen, sauberen Paket pyproject.toml Datei und unterhält eine strenge poetry.lock um den gesamten Umgebungsbaum bis auf die Prüfsumme jedes einzelnen Unterpakets einzufrieren:

(device.poetry.dependencies)
python = "^3.10"
requests = "^2.31.0"
pandas = "^2.1.0"

Und hier sind die Befehle zum Erstellen, Sperren und Ausführen von Umgebungen:

$ poetry init
$ poetry set up
$ poetry run python principal.py

// Der moderne Knowledge Science Customary (Conda)

Für moderne Knowledge-Science-Workloads sind Pakete oft auf Nicht-Python-Binärdateien angewiesen (wie C++-Bibliotheken, CUDA-Treiber oder BLAS-Suiten für lineare Algebra). Conda ist ein Umgebungs- und Paketmanager, der diese Binärdateien nahtlos isolieren und bereitstellen kann. Innerhalb eines atmosphere.yaml Datei:

identify: ml_env
channels:
  - conda-forge
dependencies:
  - python=3.10
  - numpy=1.24
  - pytorch-gpu

Hier sind die Befehle zum Erstellen und Aktivieren einer binärsicheren Umgebung:

$ conda env create -f atmosphere.yml
$ conda activate ml_env

Ausgabebeispiel (beim Ausführen poetry):

Resolving dependencies...
Writing lock file...
Efficiently locked 24 dependencies.

Wenn Sie über die Customary-Pip-Set up hinaus auf Poetry oder Conda umsteigen, wird sichergestellt, dass Ihre Anwendungs- oder Knowledge-Science-Pipeline auf dem Laptop Ihres Kollegen und in der Produktions-Cloud genauso funktioniert wie auf Ihrem lokalen Laptop computer.

# Zusammenfassung

Die Beherrschung dieser fünf Konzepte markiert den Übergang vom Schreiben von Skripten zum Erstellen von Software program. Durch die Verwendung von Typhinweisen für die Sicherheit der Codebasis, die Auswahl der richtigen Parallelitätsmodelle für Geschwindigkeit, die Nutzung funktionaler Instruments für elegante Datenflüsse, die Berücksichtigung des MRO in objektorientierten Strukturen und die Einführung moderner Umgebungssysteme für Reproduzierbarkeit heben Sie Ihr Python-Toolkit auf professionelle technische Requirements.

Matthew Mayo (@mattmayo13) hat einen Grasp-Abschluss in Informatik und ein Diplom in Knowledge Mining. Als geschäftsführender Herausgeber von KDnuggets & Statistikund Mitherausgeber bei Beherrschung des maschinellen LernensZiel von Matthew ist es, komplexe datenwissenschaftliche Konzepte zugänglich zu machen. Zu seinen beruflichen Interessen zählen die Verarbeitung natürlicher Sprache, Sprachmodelle, Algorithmen für maschinelles Lernen und die Erforschung neuer KI. Seine Mission ist es, das Wissen in der Datenwissenschaftsgemeinschaft zu demokratisieren. Matthew programmiert seit seinem sechsten Lebensjahr.



Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert