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!