PEP 290 – Code Migration und Modernisierung
- Autor:
- Raymond Hettinger <python at rcn.com>
- Status:
- Aktiv
- Typ:
- Informational
- Erstellt:
- 06-Jun-2002
- Post-History:
Zusammenfassung
Dieses PEP ist eine Sammlung von Verfahren und Ideen zur Aktualisierung von Python-Anwendungen bei der Installation neuerer Python-Versionen.
Die Migrationstipps beleuchten mögliche Kompatibilitätslücken und geben Vorschläge, wie diese Unterschiede gefunden und behoben werden können. Die Modernisierungsverfahren zeigen, wie älterer Code aktualisiert werden kann, um neue Sprachfeatures zu nutzen.
Begründung
Dieses Repository von Verfahren dient als Katalog oder Checkliste bekannter Migrationsprobleme und Verfahren zur Behandlung dieser Probleme.
Migrationsprobleme können aus mehreren Gründen auftreten. Einige veraltete Features werden gemäß den Richtlinien in PEP 4 langsam veraltet. Außerdem basiert einige Code auf undokumentierten Verhaltensweisen, die sich zwischen den Versionen ändern können. Einige Code kann auf Verhalten basieren, das sich später als Fehler herausstellte und sich ändert, wenn der Fehler behoben wird.
Modernisierungsoptionen ergeben sich, wenn neue Python-Versionen Features hinzufügen, die eine verbesserte Klarheit oder höhere Leistung als bisher ermöglichen.
Richtlinien für neue Einträge
Entwickler mit Commit-Zugriff können dieses PEP direkt aktualisieren. Andere können ihre Ideen zur möglichen Aufnahme an einen Entwickler senden.
Obwohl ein konsistentes Format das Repository einfacher zu verwenden macht, können Sie Abschnitte hinzufügen oder entfernen, um die Klarheit zu verbessern.
Grep-Muster können als Werkzeug zur Verfügung gestellt werden, um Wartende bei der Suche nach Code für mögliche Updates zu unterstützen. Vollautomatische Such-/Ersetzungs-Reguläre Ausdrücke werden jedoch nicht empfohlen. Stattdessen sollte jedes Codefragment individuell bewertet werden.
Der Abschnitt mit den Gegenanzeigen ist der wichtigste Teil eines neuen Eintrags. Er listet bekannte Situationen auf, in denen die Aktualisierung NICHT angewendet werden SOLLTE.
Migrationsprobleme
Vergleichsoperatoren nicht als Abkürzung für die Erzeugung von 0 oder 1
Vor Python 2.3 gaben Vergleichsoperationen 0 oder 1 zurück, anstatt True oder False. Einige Code könnten dies als Abkürzung für die Erzeugung von Null oder Eins verwendet haben, wo ihre booleschen Gegenstücke nicht geeignet waren. Zum Beispiel
def identity(m=1):
"""Create and m-by-m identity matrix"""
return [[i==j for i in range(m)] for j in range(m)]
In Python 2.2 würde ein Aufruf von identity(2) ergeben
[[1, 0], [0, 1]]
In Python 2.3 würde derselbe Aufruf ergeben
[[True, False], [False, True]]
Da Booleans eine Unterklasse von Integern sind, würde die Matrix weiterhin normal berechnet werden, aber sie würde nicht wie erwartet ausgegeben werden. Die List Comprehension sollte geändert werden zu
return [[int(i==j) for i in range(m)] for j in range(m)]
Es gibt ähnliche Bedenken beim Speichern von Daten, die von anderen Anwendungen verwendet werden sollen, die möglicherweise eine Zahl anstelle von True oder False erwarten.
Modernisierungsverfahren
Verfahren sind nach der Python-Version gruppiert, die benötigt wird, um von der Modernisierung profitieren zu können.
Python 2.4 oder neuer
Einfügen und Entfernen am Anfang von Listen
Pythons Listen sind so implementiert, dass sie mit `append` und `pop` am rechten Ende am besten funktionieren. Die Verwendung von pop(0) oder insert(0, x) löst eine O(n)-Datenbewegung für die gesamte Liste aus. Um diesem Bedarf entgegenzukommen, führt Python 2.4 einen neuen Container, collections.deque() ein, der effiziente `append`- und `pop`-Operationen sowohl am linken als auch am rechten Ende bietet (der Kompromiss ist ein deutlich langsamerer `getitem`/`setitem`-Zugriff). Der neue Container ist besonders hilfreich für die Implementierung von Datenwarteschlangen.
Muster
c = list(data) --> c = collections.deque(data)
c.pop(0) --> c.popleft()
c.insert(0, x) --> c.appendleft()
Lokalisierung
grep pop(0 or
grep insert(0
Vereinfachung benutzerdefinierter Sortierungen
In Python 2.4 akzeptieren die `sort`-Methode für Listen und die neue eingebaute Funktion `sorted` beide eine `key`-Funktion zur Berechnung von Sortierschlüsseln. Im Gegensatz zur `cmp`-Funktion, die bei jedem Vergleich angewendet wird, wird die Key-Funktion nur einmal auf jeden Datensatz angewendet. Sie ist viel schneller als `cmp` und typischerweise besser lesbar bei geringerem Codeaufwand. Die Key-Funktion erhält auch die Stabilität der Sortierung aufrecht (Datensätze mit demselben Schlüssel bleiben in ihrer ursprünglichen Reihenfolge).
Ursprünglicher Code mit Vergleichsfunktion
names.sort(lambda x,y: cmp(x.lower(), y.lower()))
Alternativer ursprünglicher Code mit expliziter Dekoration
tempnames = [(n.lower(), n) for n in names]
tempnames.sort()
names = [original for decorated, original in tempnames]
Überarbeiteter Code mit Key-Funktion
names.sort(key=str.lower) # case-insensitive sort
Lokalisierung: grep sort *.py
Ersetzen gängiger Lambda-Verwendungen
In Python 2.4 erhielt das `operator`-Modul zwei neue Funktionen, `itemgetter()` und `attrgetter()`, die gängige Verwendungen des `lambda`-Schlüsselworts ersetzen können. Die neuen Funktionen laufen schneller und werden von einigen als lesbarer angesehen.
Muster
lambda r: r[2] --> itemgetter(2)
lambda r: r.myattr --> attrgetter('myattr')
Typische Kontexte
sort(studentrecords, key=attrgetter('gpa')) # set a sort field
map(attrgetter('lastname'), studentrecords) # extract a field
Lokalisierung: grep lambda *.py
Vereinfachte umgekehrte Iteration
Python 2.4 führte die eingebaute Funktion `reversed` für die umgekehrte Iteration ein. Die bestehenden Ansätze zur umgekehrten Iteration litten unter Wortkargheit, Leistungsproblemen (Geschwindigkeit und Speicherverbrauch) und/oder mangelnder Klarheit. Ein bevorzugter Stil ist es, die Sequenz in Vorwärtsrichtung auszudrücken, `reversed` auf das Ergebnis anzuwenden und dann über den resultierenden schnellen, speichereffizienten Iterator zu iterieren.
Ursprünglicher Code, ausgedrückt mit halb offenen Intervallen
for i in range(n-1, -1, -1):
print seqn[i]
Alternativer ursprünglicher Code, in mehreren Schritten umgekehrt
rseqn = list(seqn)
rseqn.reverse()
for value in rseqn:
print value
Alternativer ursprünglicher Code, ausgedrückt mit erweiterten Slicings
for value in seqn[::-1]:
print value
Überarbeiteter Code mit der Funktion `reversed`
for value in reversed(seqn):
print value
Python 2.3 oder neuer
Testen der String-Mitgliedschaft
In Python 2.3, für string2 in string1, wird die Längenbeschränkung für string2 aufgehoben; sie kann jetzt ein String beliebiger Länge sein. Bei der Suche nach einem Substring, bei dem die Position des Substrings im Originalstring keine Rolle spielt, macht die Verwendung des `in`-Operators die Bedeutung klar.
Muster
string1.find(string2) >= 0 --> string2 in string1
string1.find(string2) != -1 --> string2 in string1
Ersetzen Sie apply() durch einen direkten Funktionsaufruf
In Python 2.3 wurde `apply()` als "Pending Deprecation" markiert, da es durch die Einführung von `*` und `**` in Funktionsaufrufen in Python 1.6 veraltet war. Die Verwendung eines direkten Funktionsaufrufs war schon immer etwas schneller als `apply()`, da sie die Suche nach der eingebauten Funktion einsparte. Jetzt ist `apply()` aufgrund seiner Verwendung des `warnings`-Moduls noch langsamer.
Muster
apply(f, args, kwds) --> f(*args, **kwds)
Hinweis: Die "Pending Deprecation" wurde in Python 2.3.3 aus `apply()` entfernt, da sie Probleme für Personen verursacht, die Code pflegen müssen, der mit Python-Versionen bis zurück zu 1.5.2 funktioniert, wo es keine Alternative zu `apply()` gab. Die Funktion bleibt jedoch veraltet.
Python 2.2 oder neuer
Testen der Dictionary-Mitgliedschaft
Verwenden Sie für den Test von Dictionary-Mitgliedschaften das Schlüsselwort `in` anstelle der Methode `has_key()`. Das Ergebnis ist kürzer und besser lesbar. Der Stil wird konsistent mit Tests auf Mitgliedschaft in Listen. Das Ergebnis ist geringfügig schneller, da `has_key` eine Attributsuche erfordert und einen relativ teuren Funktionsaufruf verwendet.
Muster
if d.has_key(k): --> if k in d:
Gegenanzeigen
- Einige dictionary-ähnliche Objekte definieren möglicherweise keine `__contains__()`-Methode
if dictlike.has_key(k)
Lokalisierung: grep has_key
Iteration über Dictionaries
Verwenden Sie die neuen `iter`-Methoden zum Iterieren über Dictionaries. Die `iter`-Methoden sind schneller, da sie kein neues Listenobjekt mit einer vollständigen Kopie aller Schlüssel, Werte oder Elemente erstellen müssen. Die Auswahl nur der benötigten Schlüssel, Werte oder Elemente (Schlüssel/Wert-Paare) spart Zeit für die Erstellung von Wegwerfobjekt-Referenzen und spart im Falle von Elementen eine zweite Hash-Suche des Schlüssels.
Muster
for key in d.keys(): --> for key in d:
for value in d.values(): --> for value in d.itervalues():
for key, value in d.items():
--> for key, value in d.iteritems():
Gegenanzeigen
- Wenn Sie eine Liste benötigen, ändern Sie nicht den Rückgabetyp
def getids(): return d.keys()
- Einige dictionary-ähnliche Objekte definieren möglicherweise keine `iter`-Methoden
for k in dictlike.keys():
- Iteratoren unterstützen kein Slicing, Sortieren oder andere Operationen
k = d.keys(); j = k[:]
- Dictionary-Iteratoren verbieten die Modifizierung des Dictionaries
for k in d.keys(): del[k]
stat Methoden
Ersetzen Sie `stat`-Konstanten oder -Indizes durch neue `os.stat`-Attribute und -Methoden. Die `os.stat`-Attribute und -Methoden sind nicht ordnungsabhängig und erfordern keinen Import des `stat`-Moduls.
Muster
os.stat("foo")[stat.ST_MTIME] --> os.stat("foo").st_mtime
os.stat("foo")[stat.ST_MTIME] --> os.path.getmtime("foo")
Lokalisierung: grep os.stat oder grep stat.S
Verringerung der Abhängigkeit vom types Modul
Das `types`-Modul wird wahrscheinlich in Zukunft veraltet sein. Verwenden Sie stattdessen eingebaute Konstruktorfunktionen. Diese sind möglicherweise geringfügig schneller.
Muster
isinstance(v, types.IntType) --> isinstance(v, int)
isinstance(s, types.StringTypes) --> isinstance(s, basestring)
Die vollständige Nutzung dieser Technik erfordert Python 2.3 oder neuer (`basestring` wurde in Python 2.3 eingeführt), aber Python 2.2 ist für die meisten Anwendungen ausreichend.
Lokalisierung: grep types *.py | grep import
Vermeiden Sie Variablennamen, die mit dem __builtins__ Modul kollidieren
In Python 2.2 wurden neue eingebaute Typen für `dict` und `file` hinzugefügt. Skripte sollten die Zuweisung von Variablennamen vermeiden, die diese Typen maskieren. Derselbe Rat gilt auch für bestehende eingebaute Typen wie `list`.
Muster
file = open('myfile.txt') --> f = open('myfile.txt')
dict = obj.__dict__ --> d = obj.__dict__
Lokalisierung: grep 'file ' *.py
Python 2.1 oder neuer
whrandom Modul veraltet
Alle zufallsbezogenen Methoden wurden an einem Ort, dem `random`-Modul, zusammengefasst.
Muster
import whrandom --> import random
Lokalisierung: grep whrandom
Python 2.0 oder neuer
String-Methoden
Das `string`-Modul wird wahrscheinlich in Zukunft veraltet sein. Verwenden Sie stattdessen String-Methoden. Diese sind auch schneller.
Muster
import string ; string.method(s, ...) --> s.method(...)
c in string.whitespace --> c.isspace()
Lokalisierung: grep string *.py | grep import
startswith und endswith String-Methoden
Verwenden Sie diese String-Methoden anstelle von Slicing. Es muss kein Slice erstellt werden und es besteht keine Gefahr des falschen Zählens.
Muster
"foobar"[:3] == "foo" --> "foobar".startswith("foo")
"foobar"[-3:] == "bar" --> "foobar".endswith("bar")
Das atexit Modul
Das `atexit`-Modul unterstützt die Ausführung mehrerer Funktionen beim Programmabbruch. Außerdem unterstützt es parametrisierte Funktionen. Leider steht seine Implementierung im Konflikt mit dem `sys.exitfunc`-Attribut, das nur eine einzige Exit-Funktion unterstützt. Code, der auf `sys.exitfunc` angewiesen ist, kann andere Module (einschließlich Bibliotheksmodule) stören, die sich entscheiden, das neuere und vielseitigere `atexit`-Modul zu verwenden.
Muster
sys.exitfunc = myfunc --> atexit.register(myfunc)
Python 1.5 oder neuer
Klassenbasierte Ausnahmen
String-Ausnahmen sind veraltet, leiten Sie daher von der Basisklasse `Exception` ab. Im Gegensatz zu den veralteten String-Ausnahmen leiten sich Klassen-Ausnahmen alle von einer anderen Ausnahme oder der Basisklasse `Exception` ab. Dies ermöglicht sinnvolle Gruppierungen von Ausnahmen. Es ermöglicht auch eine " `except` `Exception` "-Klausel, um alle Ausnahmen abzufangen.
Muster
NewError = 'NewError' --> class NewError(Exception): pass
Lokalisierung: Verwenden Sie PyChecker.
Alle Python-Versionen
Testen auf None
Da es nur ein einziges `None`-Objekt gibt, kann die Gleichheit mit Identität getestet werden. Identitätstests sind geringfügig schneller als Gleichheitstests. Außerdem können einige Objekttypen den Vergleich überladen, sodass Gleichheitstests viel langsamer sein können.
Muster
if v == None --> if v is None:
if v != None --> if v is not None:
Lokalisierung: grep '== None' oder grep '!= None'
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0290.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT