Warum verwendet Python-Code die Funktion len () anstelle einer Längenmethode?

204

Ich weiß, dass Python eine len()Funktion hat, mit der die Größe eines Strings bestimmt wird, aber ich habe mich gefragt, warum es keine Methode des String-Objekts ist.

Aktualisieren

Ok, mir wurde klar, dass ich mich peinlich geirrt hatte. __len__()ist eigentlich eine Methode eines String-Objekts. Es scheint nur seltsam, objektorientierten Code in Python zu sehen, der die len-Funktion für Zeichenfolgenobjekte verwendet. Darüber hinaus ist es auch seltsam, __len__den Namen statt nur len zu sehen.

fuentesjr
quelle

Antworten:

180

Strings haben eine Längenmethode: __len__()

Das Protokoll in Python besteht darin, diese Methode für Objekte zu implementieren, die eine Länge haben und die integrierte len()Funktion verwenden, die sie für Sie aufruft, ähnlich wie Sie __iter__()die integrierte iter()Funktion implementieren und verwenden würden (oder die Methode dahinter aufrufen lassen) die Szenen für Sie) auf Objekten, die iterierbar sind.

Weitere Informationen finden Sie unter Emulieren von Containertypen .

Hier ist eine gute Lektüre zum Thema Protokolle in Python: Python und das Prinzip des geringsten Erstaunens

Jonny Buchanan
quelle
124
Es wundert mich, wie schwachsinnig der Grund für die Verwendung lenist. Sie denken, dass es einfacher ist, Menschen zur Umsetzung .__len__zu zwingen , als Menschen zur Umsetzung zu zwingen .len(). Es ist das gleiche und man sieht viel sauberer aus. Wenn die Sprache eine OOP haben wird __len__, was in aller Welt ist der Sinn der len(..)
Alternative
38
len, str usw. können mit Funktionen höherer Ordnung wie Map, Reduce und Filter verwendet werden, ohne dass eine Funktion oder ein Lambda definiert werden muss, nur um eine Methode aufzurufen. Nicht alles dreht sich um OOP, selbst in Python.
Evicatos
11
Durch die Verwendung eines Protokolls können sie auch alternative Möglichkeiten zur Implementierung von Dingen bereitstellen. Sie können beispielsweise eine Iterable mit __iter__oder nur mit erstellen __getitem__und iter(x)funktionieren in beide Richtungen. Sie können mit __bool__oder ein im Bool-Kontext verwendbares Objekt erstellen __len__, bool(x)das in beiden Fällen funktioniert. Und so weiter. Ich denke , Armin dies recht gut in der verknüpften Post aber erklärt , auch wenn er nicht, rief Python moronic wegen einer außerhalben Erklärung von einem Mann, der mit dem Kern Devs uneins oft öffentlich ist nicht gerade fair sein ...
abarnert
8
@ Evicatos: +1 für "Nicht alles dreht sich um OOP", aber ich würde mit " besonders in Python" enden , nicht einmal . Python (im Gegensatz zu Java, Ruby oder Smalltalk) versucht nicht, eine "reine OOP" -Sprache zu sein. Es ist explizit als "Multi-Paradigma" -Sprache konzipiert. Listenverständnisse sind keine Methoden für Iterables. zipist eine Funktion der obersten Ebene, keine zip_withMethode. Und so weiter. Nur weil alles ein Objekt ist, heißt das nicht, dass es das Wichtigste an jeder Sache ist, ein Objekt zu sein.
Abarnert
7
Dieser Artikel ist so schlecht geschrieben, dass ich verwirrt und wütend bin, nachdem ich versucht habe, ihn zu lesen. "In Python 2.x macht der Tupel-Typ zum Beispiel keine nicht speziellen Methoden verfügbar, und dennoch können Sie damit einen String daraus machen:" Das Ganze ist eine Verschmelzung von nahezu nicht entzifferbaren Sätzen.
MirroredFate
93

Jims Antwort auf diese Frage könnte helfen; Ich kopiere es hier. Zitat von Guido van Rossum:

Zunächst habe ich aus HCI-Gründen len (x) gegenüber x.len () gewählt (def __len __ () kam viel später). Es gibt zwei miteinander verflochtene Gründe, beide HCI:

(a) Bei einigen Operationen liest sich die Präfixnotation einfach besser als die Postfix - Präfix (und Infix!) - Operationen haben in der Mathematik eine lange Tradition. Sie mögen Notationen, bei denen die visuellen Elemente dem Mathematiker helfen, über ein Problem nachzudenken. Vergleichen Sie die Leichtigkeit, mit der wir eine Formel wie x * (a + b) in x a + x b umschreiben, mit der Ungeschicklichkeit, dasselbe mit einer rohen OO-Notation zu tun.

(b) Wenn ich Code lese, der len (x) sagt, weiß ich, dass er nach der Länge von etwas fragt. Dies sagt mir zwei Dinge: Das Ergebnis ist eine ganze Zahl, und das Argument ist eine Art Container. Im Gegenteil, wenn ich x.len () lese, muss ich bereits wissen, dass x eine Art Container ist, der eine Schnittstelle implementiert oder von einer Klasse mit einem Standard len () erbt. Erleben Sie die Verwirrung, die wir gelegentlich haben, wenn eine Klasse, die kein Mapping implementiert, eine get () - oder keys () -Methode hat oder etwas, das keine Datei ist, eine write () -Methode hat.

Wenn ich dasselbe auf eine andere Weise sage, sehe ich 'len' als eine eingebaute Operation. Ich würde es hassen, das zu verlieren. /… /

Federico A. Ramponi
quelle
37

Es gibt eine lenMethode:

>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>
unmontiert
quelle
34

Python ist eine pragmatische Programmiersprache, und die Gründe für len()eine Funktion ist , und kein Verfahren str, list, dictusw. sind pragmatisch.

Die len()integrierte Funktion befasst sich direkt mit integrierten Typen: Die CPython-Implementierung von gibt len()tatsächlich den Wert des ob_sizeFelds in der PyVarObjectC-Struktur zurück , das jedes integrierte Objekt mit variabler Größe im Speicher darstellt. Dies ist viel schneller als das Aufrufen einer Methode - es muss keine Attributsuche durchgeführt werden. Immer die Anzahl der Elemente in einer Sammlung ist ein gemeinsamer Betrieb und müssen effizient für eine solche grundlegende und vielfältige Arten wie arbeiten str, list, array.arrayusw.

Um die Konsistenz zu fördern len(o), wird Python bei der Anwendung auf einen benutzerdefinierten Typ o.__len__()als Fallback aufgerufen. __len__, __abs__Dokumentiert und alle anderen speziellen Methoden in der Python - Datenmodell machen es einfach , Objekte, die sich verhalten wie die Einbauten zu schaffen, so dass die ausdrucksstark und sehr konsistent APIs wir „Pythonic“ nennen.

Durch die Implementierung spezieller Methoden können Ihre Objekte Iteration unterstützen, Infix-Operatoren überladen, Kontexte in withBlöcken verwalten usw. Sie können sich das Datenmodell als eine Möglichkeit vorstellen, die Python-Sprache selbst als Framework zu verwenden, in das die von Ihnen erstellten Objekte nahtlos integriert werden können.

Ein zweiter Grund, der durch Zitate von Guido van Rossum wie diesem gestützt wird , ist, dass es einfacher zu lesen und zu schreiben ist len(s)als s.len().

Die Notation len(s)stimmt mit unären Operatoren mit Präfixnotation überein, wie z abs(n). len()wird viel häufiger verwendet als abs(), und es verdient, so einfach zu schreiben zu sein.

Es kann auch einen historischen Grund geben: In der ABC-Sprache, die Python vorausging (und deren Design sehr einflussreich war), gab es einen unären Operator, der so geschrieben wurde, wie es #sbedeutete len(s).

Luciano Ramalho
quelle
12
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.
Alex Coventry
quelle
34
Das Offensichtliche bei der Arbeit mit einem Objekt ist natürlich eine Methode.
Peter Cooper
4
@Peter: Ich würde jedem, der einen Fotobeweis dafür hat, dass er Ihren Kommentar auf Guidos Rücken geklebt hat, 20 US-Dollar zahlen. 50 Dollar, wenn es auf seiner Stirn liegt.
Fehler
1
Ja, die Python-Designer halten an Dogmen fest, aber sie selbst respektieren ihr eigenes Dogma nicht.
Bitek
2
$ python -c 'import this' | grep offensichtlich
Ed Randall
4

Hier gibt es einige großartige Antworten, und bevor ich meine eigenen gebe, möchte ich einige der Edelsteine ​​hervorheben (kein Rubinwortspiel beabsichtigt), die ich hier gelesen habe.

  • Python ist keine reine OOP-Sprache - es ist eine universelle Mehrparadigmensprache, mit der der Programmierer das Paradigma verwenden kann, mit dem er sich am besten auskennt, und / oder das Paradigma, das für seine Lösung am besten geeignet ist.
  • Python hat erstklassige Funktionen, lenist also eigentlich ein Objekt. Ruby hingegen hat keine erstklassigen Funktionen. Das lenFunktionsobjekt verfügt also über eigene Methoden, die Sie durch Ausführen überprüfen können dir(len).

Wenn Ihnen die Funktionsweise in Ihrem eigenen Code nicht gefällt, ist es für Sie trivial, die Container mit Ihrer bevorzugten Methode erneut zu implementieren (siehe Beispiel unten).

>>> class List(list):
...     def len(self):
...         return len(self)
...
>>> class Dict(dict):
...     def len(self):
...         return len(self)
...
>>> class Tuple(tuple):
...     def len(self):
...         return len(self)
...
>>> class Set(set):
...     def len(self):
...         return len(self)
...
>>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'])
>>> my_dict = Dict({'key': 'value', 'site': 'stackoverflow'})
>>> my_set = Set({1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'})
>>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'))
>>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple))
>>>
>>> for container in my_containers:
...     print container.len()
...
15
2
15
15
Charles Addis
quelle
0

Du kannst auch sagen

>> x = 'test'
>> len(x)
4

Verwenden von Python 2.7.3.

SpanosAngelos
quelle
9
lenFunktion wurde bereits in früheren Antworten erwähnt. Was ist dann der Sinn dieser "Antwort"?
Piotr Dobrogost
0

In den restlichen Antworten fehlt hier etwas: Die lenFunktion prüft, ob die __len__Methode ein nicht negatives Ergebnis zurückgibt int. Die Tatsache, dass lenes sich um eine Funktion handelt, bedeutet, dass Klassen dieses Verhalten nicht überschreiben können, um die Überprüfung zu vermeiden. Als solches len(obj)gibt es ein Sicherheitsniveau, obj.len()das nicht möglich ist.

Beispiel:

>>> class A:
...     def __len__(self):
...         return 'foo'
...
>>> len(A())
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    len(A())
TypeError: 'str' object cannot be interpreted as an integer
>>> class B:
...     def __len__(self):
...         return -1
... 
>>> len(B())
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    len(B())
ValueError: __len__() should return >= 0

Natürlich ist es möglich, die lenFunktion durch erneutes Zuweisen als globale Variable zu "überschreiben" , aber Code, der dies tut, ist viel offensichtlicher verdächtig als Code, der eine Methode in einer Klasse überschreibt.

kaya3
quelle
-1

Es nicht?

>>> "abc".__len__()
3
Nick Stinemates
quelle