Sollten Strings in Python iterierbar sein?

Und Guido schuf Strings im Bild von C, im Bild von Arrays von Charakteren, die sie schufen. Und Guido sah, dass es gut war. Oder nicht?

Stellen Sie sich vor, Sie schreiben vollständig idiomatischen Code, um einige Daten mit Verschachtelung zu umgehen. Schön ist besser als hässlich, einfach ist besser als komplex, also hören Sie bei der folgenden Version des Codes auf:

from collections.abc import Iterable def traverse(list_or_value, callback): if isinstance(list_or_value, Iterable): for item in list_or_value: traverse(item, callback) else: callback(list_or_value) 

Sie schreiben einen Unit-Test und was würden Sie denken? Es funktioniert nicht und funktioniert nicht nur nicht, sondern

 >>> traverse({"status": "ok"}, print) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in traverse File "<stdin>", line 4, in traverse File "<stdin>", line 4, in traverse [Previous line repeated 989 more times] File "<stdin>", line 2, in traverse File "/usr/local/opt/python/libexec/bin/../../Frameworks/Python.framework/Versions/3.7/lib/python3.7/abc.py", line 139, in __instancecheck__ return _abc_instancecheck(cls, instance) RecursionError: maximum recursion depth exceeded in comparison 

Wie? Warum? Auf der Suche nach einer Antwort tauchen Sie in die wunderbare Welt der Sammlungen unendlicher Tiefe ein.

Tatsächlich ist eine Zeichenfolge die einzige integrierte Iterable , die Iterable immer als Element zurückgibt! Wir können natürlich ein weiteres Beispiel erstellen, indem wir eine Liste erstellen und ein- oder zweimal zu uns selbst hinzufügen. Aber sehen Sie dies häufig in Ihrem Code? Und die Linie ist Iterable unendlicher Tiefe und schleicht sich unter dem Iterable Nacht direkt in Ihre Produktion ein.

Ein weiteres Beispiel. Irgendwo im Code mussten Sie wiederholt prüfen, ob Elemente in Containern vorhanden sind. Sie beschließen, einen Helfer zu schreiben, der ihn in vielerlei Hinsicht beschleunigt. Sie schreiben eine universelle Lösung, die nur die Methode __contains__ verwendet (die einzige Methode in der abstrakten __contains__ des Container ), entscheiden sich dann jedoch für die __contains__ für einen speziellen Fall - eine Sammlung. Schließlich können Sie einfach daran entlang gehen und ein set !

 import functools from typing import Collection, Container def faster_container(c: Container) -> Container: if isinstance(c, Collection): return set(c) return CachedContainer(c) class CachedContainer(object): def __init__(self, c: Container): self._contains = functools.lru_cache()(c.__contains__) def __contains__(self, stuff): return self._contains(stuff) 

III ... Ihre Lösung funktioniert nicht! Bitte schön! Schon wieder!

 >>> c = faster_container(othello_text) >>> "Have you pray'd to-night, Desdemona?" in c False 

(Aber die falsche Antwort wurde sehr schnell ausgegeben ...)

Warum? Weil eine Zeichenfolge in Python eine erstaunliche Sammlung ist, in der die Semantik der Methode __contains__ nicht mit der Semantik von __iter__ und __len__ .

Tatsächlich ist eine Zeichenfolge eine Sammlung:

 >>> from collections.abc import Collection >>> issubclass(str, Collection) True 

Aber die Sammlung ... was? __iter__ und __len__ betrachten dies als eine Sammlung von Zeichen:

 >>> s = "foo" >>> len(s) 3 >>> list(s) ['f', 'o', 'o'] 

Aber __contains__ denkt, dass dies eine Sammlung von __contains__ ist!

 >>> "oo" in s True >>> "oo" in list(s) False 

Was kann getan werden?


Obwohl das Verhalten von str.__contains__ im Kontext von __contains__ Implementierungen __contains__ andere Standardtypen seltsam erscheint, ist dieses Verhalten eines von vielen kleinen Dingen, die Python so bequem wie eine Skriptsprache machen. So können Sie schnell und literarisch Code darauf schreiben. Ich würde nicht empfehlen, das Verhalten dieser Methode zu ändern, zumal wir sie fast nie verwenden, um das Vorhandensein eines einzelnen Zeichens in einer Zeichenfolge zu überprüfen.

Und wissen Sie übrigens warum? Weil wir fast nie eine Zeichenfolge als Sammlung von Zeichen in einer Skriptsprache verwenden! Manipulieren bestimmter Zeichen in einer Zeichenfolge, Zugriff über den Index - meistens das Schicksal von Aufgaben in Interviews. Vielleicht sollten Sie __iter__ aus der Zeichenfolge entfernen und hinter einer Methode wie .chars() verstecken. Dies würde diese beiden Probleme lösen.

Zeit für Freitag Diskussion in den Kommentaren!

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


All Articles