
Bild von Autor | Ideogramm
Wenn Sie eine Weile in Python codiert haben, haben Sie wahrscheinlich die Grundlagen gemeistert und einige Projekte gebaut. Und jetzt schauen Sie sich Ihren Code an und denken: „Das funktioniert, aber … es ist nicht genau etwas, das ich in einer Codebewertung stolz zeigen würde.“ Wir waren alle dort.
Wenn Sie jedoch weiter codieren, wird das Schreiben sauberer Code genauso wichtig wie das Schreiben von Funktionscode. In diesem Artikel habe ich praktische Techniken zusammengestellt, mit denen Sie von „It Runs, berühren Sie es nicht“ nach „Dies ist tatsächlich aufrechterhalten“.
🔗 Hyperlink zum Code auf GitHub
1. Modelldaten explizit. Gehen Sie nicht um Diktate herum
Wörterbücher sind in Python tremendous flexibel und das ist genau das Drawback. Wenn Sie Uncooked -Wörterbücher während Ihres gesamten Codes übergeben, laden Sie Tippfehler, Schlüsselfehler und Verwirrung darüber ein, welche Daten tatsächlich vorhanden sein sollten.
Anstelle davon:
def process_user(user_dict):
if user_dict('standing') == 'lively': # What if 'standing' is lacking?
send_email(user_dict('electronic mail')) # What if it is 'mail' in some locations?
# Is it 'title', 'full_name', or 'username'? Who is aware of!
log_activity(f"Processed {user_dict('title')}")
Dieser Code ist nicht strong, da er davon ausgeht, dass Wörterbuchschlüssel ohne Validierung existieren. Es bietet keinen Schutz gegen Tippfehler oder fehlende Schlüssel, was verursacht wird KeyError Ausnahmen zur Laufzeit. Es gibt auch keine Dokumentation darüber, welche Felder erwartet werden.
Tun Sie das:
from dataclasses import dataclass
from typing import Non-obligatory
@dataclass
class Person:
id: int
electronic mail: str
full_name: str
standing: str
last_login: Non-obligatory(datetime) = None
def process_user(person: Person):
if person.standing == 'lively':
send_email(person.electronic mail)
log_activity(f"Processed {person.full_name}")
Python @dataclass Der Dekorateur bietet Ihnen eine saubere, explizite Struktur mit minimaler Kesselplatte. Ihre IDE kann jetzt eine automatische Vervollständigung für Attribute bereitstellen, und Sie erhalten unmittelbare Fehler, wenn die erforderlichen Felder fehlen.
Für eine komplexere Validierung sollten Sie Pydantic betrachten:
from pydantic import BaseModel, EmailStr, validator
class Person(BaseModel):
id: int
electronic mail: EmailStr # Validates electronic mail format
full_name: str
standing: str
@validator('standing')
def status_must_be_valid(cls, v):
if v not in {'lively', 'inactive', 'pending'}:
elevate ValueError('Have to be lively, inactive or pending')
return v
Jetzt überprüft sich Ihre Daten selbst, fängt Fehler frühzeitig auf und dokumentiert die Erwartungen klar.
2. Verwenden Sie Enums für bekannte Entscheidungen
String -Literale sind anfällig für Tippfehler und bieten keine IDE -Autokonomplete. Die Validierung erfolgt nur zur Laufzeit.
Anstelle davon:
def process_order(order, standing):
if standing == 'pending':
# course of logic
elif standing == 'shipped':
# totally different logic
elif standing == 'delivered':
# extra logic
else:
elevate ValueError(f"Invalid standing: {standing}")
# Later in your code...
process_order(order, 'shiped') # Typo! However no IDE warning
Tun Sie das:
from enum import Enum, auto
class OrderStatus(Enum):
PENDING = 'pending'
SHIPPED = 'shipped'
DELIVERED = 'delivered'
def process_order(order, standing: OrderStatus):
if standing == OrderStatus.PENDING:
# course of logic
elif standing == OrderStatus.SHIPPED:
# totally different logic
elif standing == OrderStatus.DELIVERED:
# extra logic
# Later in your code...
process_order(order, OrderStatus.SHIPPED) # IDE autocomplete helps!
Wenn Sie sich mit einer festen Reihe von Optionen befassen, macht ein Enum Ihren Code robuster und Selbstdokumentation.
Mit Aufzügen:
- Ihre IDE bietet automatische Vorschläge
- Tippfehler werden (quick) unmöglich
- Sie können bei Bedarf alle möglichen Werte durchsetzen
Enum erstellt eine Reihe genannter Konstanten. Der Typ Hinweis standing: OrderStatus dokumentiert den erwarteten Parametertyp. Verwendung OrderStatus.SHIPPED Anstelle eines Saitenliterales ermöglicht die IDE automatisch und fängt Tippfehler zur Entwicklungszeit.
3. Verwenden Sie nur Key phrase-Argumente für Klarheit
Das versatile Argumentsystem von Python ist leistungsfähig, kann jedoch zu Verwirrung führen, wenn Funktionsaufrufe mehrere optionale Parameter aufweisen.
Anstelle davon:
def create_user(title, electronic mail, admin=False, notify=True, momentary=False):
# Implementation
# Later in code...
create_user("John Smith", "john@instance.com", True, False)
Warten Sie, was meinen diese Booleschen wieder?
Bei der Aufforderung mit Positionsargumenten ist unklar, welche Booleschen Werte ohne Überprüfung der Funktionsdefinition darstellen. Gilt für Administrator, Benachrichtigung oder etwas anderes?
Tun Sie das:
def create_user(title, electronic mail, *, admin=False, notify=True, momentary=False):
# Implementation
# Now you could use key phrases for non-compulsory args
create_user("John Smith", "john@instance.com", admin=True, notify=False)
Die Syntax erzwingt alle Argumente, nachdem sie nach Schlüsselwort angegeben werden. Auf diese Weise ruft Ihre Funktion Selbstdokumentation auf und verhindert das Drawback „Thriller Boolean“, bei dem die Leser nicht sagen können, was wahr oder Falsch bezieht, ohne die Funktionsdefinition zu lesen.
Dieses Muster ist besonders nützlich bei API -Aufrufen und dergleichen, wo Sie die Klarheit auf der Anrufe sicherstellen möchten.
4. Verwenden Sie Pathlib über OS.Path
Pythons OS.Path -Modul ist funktional, aber klobig. Das neuere Pathlib-Modul bietet einen objektorientierten Ansatz, der intuitiver und weniger fehleranfällig ist.
Anstelle davon:
import os
data_dir = os.path.be a part of('knowledge', 'processed')
if not os.path.exists(data_dir):
os.makedirs(data_dir)
filepath = os.path.be a part of(data_dir, 'output.csv')
with open(filepath, 'w') as f:
f.write('resultsn')
# Test if we now have a JSON file with the identical title
json_path = os.path.splitext(filepath)(0) + '.json'
if os.path.exists(json_path):
with open(json_path) as f:
knowledge = json.load(f)
Dies verwendet eine String -Manipulation mit os.path.be a part of() Und os.path.splitext() für Pfadbeschaffung. Pfadoperationen sind über verschiedene Funktionen hinweg verstreut. Der Code ist ausführlich und weniger intuitiv.
Tun Sie das:
from pathlib import Path
data_dir = Path('knowledge') / 'processed'
data_dir.mkdir(mother and father=True, exist_ok=True)
filepath = data_dir / 'output.csv'
filepath.write_text('resultsn')
# Test if we now have a JSON file with the identical title
json_path = filepath.with_suffix('.json')
if json_path.exists():
knowledge = json.masses(json_path.read_text())
Warum Pathlib besser ist:
- Pfad beiträgt / ist intuitiverer
- Methoden wie
mkdir()Anwesendexists()Undread_text()sind an das Pfadobjekt angehängt - Operationen wie Änderungen von Erweiterungen (mit _suffix) sind semantischer
Pathlib behandelt die Feinheiten der Pfadmanipulation über verschiedene Betriebssysteme hinweg. Dies macht Ihren Code tragbarer und robuster.
5. MIT WARD -Klauseln schnell scheitern
Tief verschachtelte, wenn es sich um Andränge handelt, sind oft schwer zu verstehen und aufrechtzuerhalten. Die Verwendung von frühen Renditen – Schutzklauseln – führt zu mehr lesbarer Code.
Anstelle davon:
def process_payment(order, person):
if order.is_valid:
if person.has_payment_method:
payment_method = person.get_payment_method()
if payment_method.has_sufficient_funds(order.whole):
strive:
payment_method.cost(order.whole)
order.mark_as_paid()
send_receipt(person, order)
return True
besides PaymentError as e:
log_error(e)
return False
else:
log_error("Inadequate funds")
return False
else:
log_error("No fee technique")
return False
else:
log_error("Invalid order")
return False
Tiefe Verschachtelung ist schwer zu folgen. Jeder bedingte Block erfordert die gleichzeitige Verfolgung mehrerer Zweige.
Tun Sie das:
def process_payment(order, person):
# Guard clauses: test preconditions first
if not order.is_valid:
log_error("Invalid order")
return False
if not person.has_payment_method:
log_error("No fee technique")
return False
payment_method = person.get_payment_method()
if not payment_method.has_sufficient_funds(order.whole):
log_error("Inadequate funds")
return False
# Principal logic comes in any case validations
strive:
payment_method.cost(order.whole)
order.mark_as_paid()
send_receipt(person, order)
return True
besides PaymentError as e:
log_error(e)
return False
Schutzklauseln verarbeiten Fehlerfälle vorne und reduzieren die Einkleberwerte. Jede Bedingung wird nacheinander überprüft, wodurch der Fluss leichter zu folgen ist. Die Hauptlogik kommt am Ende und ist eindeutig von der Fehlerbehandlung getrennt.
Dieser Ansatz skaliert viel besser, wenn Ihre Logik in Komplexität wächst.
6. Nicht überbeanspruchte Auflistverständnisse
Record -Verständnisse sind eine der elegantesten Merkmale von Python, werden jedoch bei Überladen mit komplexen Bedingungen oder Transformationen unlesbar.
Anstelle davon:
# Laborious to parse at a look
active_premium_emails = (person('electronic mail') for person in users_list
if person('standing') == 'lively' and
person('subscription') == 'premium' and
person('email_verified') and
not person('electronic mail') in blacklisted_domains)
In dieser Liste wird das Verständnis zu viel Logik in eine Zeile eingebaut. Es ist schwer zu lesen und zu debuggen. Mehrere Bedingungen werden miteinander verkettet, was es schwierig macht, die Filterkriterien zu verstehen.
Tun Sie das:
Hier sind bessere Alternativen.
Choice 1: Funktion mit einem beschreibenden Namen
Extrahiert die komplexe Bedingung in eine benannte Funktion mit einem beschreibenden Namen. Das Listenverständnis ist jetzt viel klarer und konzentriert sich auf das, was es tut (E -Mails extrahieren), anstatt wie es Filterung ist.
def is_valid_premium_user(person):
return (person('standing') == 'lively' and
person('subscription') == 'premium' and
person('email_verified') and
not person('electronic mail') in blacklisted_domains)
active_premium_emails = (person('electronic mail') for person in users_list if is_valid_premium_user(person))
Choice 2: herkömmliche Schleife, wenn die Logik komplex ist
Verwendet eine traditionelle Schleife mit frühes Fortsetzung für Klarheit. Jede Bedingung wird separat überprüft, sodass es leicht zu debuggen kann, welcher Zustand möglicherweise fehlschlägt. Die Transformationslogik ist ebenfalls deutlich getrennt.
active_premium_emails = ()
for person in users_list:
# Complicated filtering logic
if person('standing') != 'lively':
proceed
if person('subscription') != 'premium':
proceed
if not person('email_verified'):
proceed
if person('electronic mail') in blacklisted_domains:
proceed
# Complicated transformation logic
electronic mail = person('electronic mail').decrease().strip()
active_premium_emails.append(electronic mail)
Auflistenverständnisse sollten Ihren Code lesbarer machen, nicht weniger. Wenn die Logik komplex wird:
- Komplexe Bedingungen in benannte Funktionen unterteilen
- Erwägen Sie, eine reguläre Schleife mit frühen Fortsetzung zu verwenden
- Teilen Sie komplexe Operationen in mehrere Schritte auf
Denken Sie daran, das Ziel ist die Lesbarkeit.
7. Schreiben Sie wiederverwendbare reine Funktionen
Eine Funktion ist eine reine Funktion, wenn sie immer den gleichen Ausgang für die gleichen Eingänge erzeugt. Außerdem hat es keine Nebenwirkungen.
Anstelle davon:
total_price = 0 # World state
def add_item_price(item_name, amount):
world total_price
# Lookup value from world stock
value = stock.get_item_price(item_name)
# Apply low cost
if settings.discount_enabled:
value *= 0.9
# Replace world state
total_price += value * amount
# Later in code...
add_item_price('widget', 5)
add_item_price('gadget', 3)
print(f"Complete: ${total_price:.2f}")
Dies verwendet den globalen Staat (total_price) was es schwierig macht.
Die Funktion hat Nebenwirkungen (modifizieren globaler Zustand) und hängt vom externen Zustand (Inventar und Einstellungen) ab. Dies macht es unvorhersehbar und schwer wiederzuverwenden.
Tun Sie das:
def calculate_item_price(merchandise, value, amount, low cost=0):
"""Calculate closing value for a amount of things with non-compulsory low cost.
Args:
merchandise: Merchandise identifier (for logging)
value: Base unit value
amount: Variety of gadgets
low cost: Low cost as decimal
Returns:
Ultimate value after reductions
"""
discounted_price = value * (1 - low cost)
return discounted_price * amount
def calculate_order_total(gadgets, low cost=0):
"""Calculate whole value for a set of things.
Args:
gadgets: Record of (item_name, value, amount) tuples
low cost: Order-level low cost
Returns:
Complete value in any case reductions
"""
return sum(
calculate_item_price(merchandise, value, amount, low cost)
for merchandise, value, amount in gadgets
)
# Later in code...
order_items = (
('widget', stock.get_item_price('widget'), 5),
('gadget', stock.get_item_price('gadget'), 3),
)
whole = calculate_order_total(order_items,
low cost=0.1 if settings.discount_enabled else 0)
print(f"Complete: ${whole:.2f}")
Die folgende Model verwendet reine Funktionen, die alle Abhängigkeiten als Parameter betrachten.
8. Schreiben Sie Docstrings für öffentliche Funktionen und Klassen
Die Dokumentation ist (und sollte nicht) ein nachträglicher Gedanke sein. Es ist ein zentraler Bestandteil des Code -Code. Gute Docstrings erklären nicht nur, was Funktionen tun, sondern warum sie existieren und wie sie sie richtig einsetzen.
Anstelle davon:
def celsius_to_fahrenheit(celsius):
"""Convert Celsius to Fahrenheit."""
return celsius * 9/5 + 32
Dies ist eine minimale Dokumentation, die den Funktionsnamen nur wiederholt. Bietet keine Informationen zu Parametern, Rückgabetwerten oder Randfällen.
Tun Sie das:
def celsius_to_fahrenheit(celsius):
"""
Convert temperature from Celsius to Fahrenheit.
The system used is: F = C × (9/5) + 32
Args:
celsius: Temperature in levels Celsius (will be float or int)
Returns:
Temperature transformed to levels Fahrenheit
Instance:
>>> celsius_to_fahrenheit(0)
32.0
>>> celsius_to_fahrenheit(100)
212.0
>>> celsius_to_fahrenheit(-40)
-40.0
"""
return celsius * 9/5 + 32
Ein guter Docstring:
- Dokumente Parameter und Rückgabewerte
- Stellt alle Ausnahmen fest, die möglicherweise erhoben werden könnten
- Bietet Nutzungsbeispiele
Ihre Docstrings dienen als ausführbare Dokumentation, die mit Ihrem Code synchronisiert bleibt.
9. Automatisieren Sie das Leinen und Formatieren
Verlassen Sie sich nicht auf die manuelle Inspektion, um Stilprobleme und gemeinsame Fehlern zu fangen. Automatische Instruments können die mühsame Arbeit der Sicherstellung von Codequalität und -konsistenz erledigen.
Sie können versuchen, diese Linien- und Formatierungswerkzeuge einzurichten:
- Schwarz – Codeformatierer
- Halskrause – Schneller Stoßunter seinen
- Mypy – statischer Typ Checker
- ISORT – Veranstalter importieren
Integrieren Sie sie mithilfe von Pre-Commit-Hooks, um vor jedem Commit automatisch Code zu überprüfen und zu formatieren:
- Vorkommind installieren:
pip set up pre-commit - Erstellen a
.pre-commit-config.yamlDatei mit den konfigurierten Instruments - Laufen
pre-commit set upzu aktivieren
Dieses Setup gewährleistet einen konsistenten Codestil und fängt ohne manuelle Aufwand frühzeitig Fehler auf.
Sie können überprüfen 7 Instruments zum Schreiben eines besseren Python -Code mehr darüber zu wissen.
10. Vermeiden Sie alles, außer dass
Generische Ausnahmehändler verbergen Fehler und erschweren das Debuggen. Sie fangen alles auf, einschließlich Syntaxfehler, Speicherfehler und Tastaturinterrudeln.
Anstelle davon:
strive:
user_data = get_user_from_api(user_id)
process_user_data(user_data)
save_to_database(user_data)
besides:
# What failed? We'll by no means know!
logger.error("One thing went mistaken")
Dies verwendet eine bloße Ausnahme zum Handeln:
- Programmierfehler (wie Syntaxfehler)
- Systemfehler (wie MemoryError)
- Tastaturinterrudeln (Strg+C)
- Erwartete Fehler (wie Netzwerkzeitüberschreitungen)
Dies erschwert das Debuggen extrem, da alle Fehler gleich behandelt werden.
Tun Sie das:
strive:
user_data = get_user_from_api(user_id)
process_user_data(user_data)
save_to_database(user_data)
besides ConnectionError as e:
logger.error(f"API connection failed: {e}")
# Deal with API connection points
besides ValueError as e:
logger.error(f"Invalid person knowledge obtained: {e}")
# Deal with validation points
besides DatabaseError as e:
logger.error(f"Database error: {e}")
# Deal with database points
besides Exception as e:
# Final resort for surprising errors
logger.essential(f"Surprising error processing person {user_id}: {e}",
exc_info=True)
# Presumably re-raise or deal with generically
elevate
Fängt spezifische Ausnahmen auf, die zu erwarten sind und angemessen behandelt werden können. Jeder Ausnahmetyp hat seine eigene Fehlermeldung und Handhabungsstrategie.
Die letzte Ausnahme außer Ausnahme fängt unerwartete Fehler auf und protokolliert sie mit vollem Traceback (exc_info=True) und räumen sie wieder, um vermeiden, ernsthafte Probleme nonetheless zu ignorieren.
Wenn Sie aus irgendeinem Grund einen All-Hap-Handler benötigen, verwenden Sie besides Exception as e: eher als bloß besides:und protokollieren Sie immer die vollständigen Ausnahmendetails mit exc_info=True.
Einpacken
Ich hoffe, Sie können zumindest einige dieser Praktiken in Ihrem Code verwenden. Beginnen Sie mit der Implementierung in Ihren Projekten.
Sie werden feststellen, dass Ihr Code wartbarer, nachweisbarer und leichter zu argumentierbarer wird.
Wenn Sie das nächste Mal versucht sind, eine Abkürzung zu nehmen, denken Sie daran: Code wird viel mehrmals gelesen als geschrieben. Joyful Clear Coding?
Bala Priya c ist ein Entwickler und technischer Schriftsteller aus Indien. Sie arbeitet gern an der Schnittstelle zwischen Mathematik, Programmierung, Datenwissenschaft und Inhaltserstellung. Ihre Interessensgebiete und Fachgebiete umfassen DevOps, Information Science und natürliche Sprachverarbeitung. Sie liest gerne, schreibt, codieren und Kaffee! Derzeit arbeitet sie daran, ihr Wissen mit der Entwicklergemeinschaft zu lernen und zu teilen, indem sie Tutorials, Anleitungen, Meinungsstücke und vieles mehr autorisiert. Bala erstellt auch ansprechende Ressourcenübersichten und Codierungs -Tutorials.
