Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 290 – Code Migration und Modernisierung

Autor:
Raymond Hettinger <python at rcn.com>
Status:
Aktiv
Typ:
Informational
Erstellt:
06-Jun-2002
Post-History:


Inhaltsverzeichnis

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

  1. 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

  1. Wenn Sie eine Liste benötigen, ändern Sie nicht den Rückgabetyp
    def getids():  return d.keys()
    
  2. Einige dictionary-ähnliche Objekte definieren möglicherweise keine `iter`-Methoden
    for k in dictlike.keys():
    
  3. Iteratoren unterstützen kein Slicing, Sortieren oder andere Operationen
    k = d.keys(); j = k[:]
    
  4. 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'


Quelle: https://github.com/python/peps/blob/main/peps/pep-0290.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT