Je sais que python a une fonction len()
utilisée pour déterminer la taille d'une chaîne, mais je me demandais pourquoi ce n'est pas une méthode de l'objet chaîne.
Ok, je me suis rendu compte que je me suis trompé de façon embarrassante. __len__()
est en fait une méthode d'un objet chaîne. Il semble étrange de voir du code orienté objet dans Python à l'aide de la fonction len sur les objets chaîne. En outre, il est également étrange de voir __len__
comme nom plutôt que simplement len.
Les chaînes ont une méthode de longueur: __len__()
Le protocole dans Python consiste à implémenter cette méthode sur des objets de longueur et à utiliser la fonction intégrée len()
, qui l'appelle pour vous, de la même manière que vous l'implémenteriez. __iter__()
et utilisez la fonction iter()
intégrée (ou faites appeler la méthode en arrière-plan pour vous) sur des objets qui sont itératifs.
Voir Emulation des types de conteneur pour plus d'informations.
Voici une bonne lecture sur le sujet des protocoles en Python: Python et le principe de moindre étonnement
La réponse de Jim à cette question peut aider; Je le copie ici. Citant Guido van Rossum:
Tout d’abord, j’ai choisi len (x) plutôt que x.len () pour des raisons liées à HCI (def __len __ () est venu beaucoup plus tard). Il y a deux raisons étroitement liées, les deux HCI:
(a) Pour certaines opérations, la notation de préfixe se lit simplement mieux que celle de postfix. Les opérations de préfixe (et infixe!) ont une longue tradition mathématique qui aime les notations où les éléments visuels aident le mathématicien à réfléchir à un problème. Comparez la facilité avec laquelle nous réécrivons une formule comme x * (a + b) en x a + x b à la maladresse de faire la même chose en utilisant une notation brute OO.
(b) Quand je lis le code qui dit len (x), je sais qu'il demande la longueur de quelque chose. Cela me dit deux choses: le résultat est un entier et l'argument est une sorte de conteneur. Au contraire, quand je lis x.len (), je dois déjà savoir que x est une sorte de conteneur implémentant une interface ou héritant d'une classe ayant un len () standard. Observez la confusion que nous avons parfois quand une classe qui n'implémente pas de mappage a une méthode get () ou keys (), ou quelque chose qui n'est pas un fichier a une méthode write ().
Dire la même chose d’une autre manière, je considère "len" comme une opération intégrée. Je détesterais perdre cela. /… /
Il existe une méthode len
:
>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>
Python est un langage de programmation pragmatique, et les raisons pour lesquelles len()
est une fonction et non une méthode de str
, list
, dict
etc. sont pragmatiques.
La fonction intégrée len()
traite directement des types intégrés: l'implémentation CPython de len()
renvoie en fait la valeur du champ ob_size
dans la structure PyVarObject
C = qui représente tout objet intégré de taille variable en mémoire. C'est beaucoup plus rapide que d'appeler une méthode - aucune recherche d'attribut n'est nécessaire. Obtenir le nombre d'éléments dans une collection est une opération courante et doit fonctionner efficacement pour des types de base aussi variés que str
, list
, array.array
etc.
Toutefois, pour favoriser la cohérence, lorsque vous appliquez len(o)
à un type défini par l'utilisateur, Python appelle o.__len__()
comme solution de secours. __len__
, __abs__
et toutes les autres méthodes spéciales décrites dans le Modèle de données Python facilitent la création d’objets se comportant comme les éléments intégrés, permettant ainsi l’expression très cohérente. Les API que nous appelons "Pythonic".
En implémentant des méthodes spéciales, vos objets peuvent prendre en charge l'itération, surcharger les opérateurs d'infix, gérer les contextes dans des blocs with
, etc. en utilisant le langage Python lui-même en tant que cadre dans lequel les objets que vous créez peuvent être intégrés de manière transparente.
Une deuxième raison, appuyée par des citations de Guido van Rossum comme celle-ci , est qu’il est plus facile de lire et d’écrire len(s)
que s.len()
.
La notation len(s)
est cohérente avec les opérateurs unaires à préfixe, tels que abs(n)
. len()
est utilisé beaucoup plus souvent que abs()
, et il mérite d'être aussi facile à écrire.
Il peut également y avoir une raison historique: dans le langage ABC qui précédait Python (et avait une grande influence dans sa conception), il y avait un opérateur unaire écrit comme #s
qui signifiait len(s)
.
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.
Il y a quelques bonnes réponses ici, et donc avant de donner la mienne, j'aimerais souligner quelques-uns des joyaux que j'ai lus ici (pas de Ruby voulu).
len
est en fait un objet. Ruby, d'autre part, n'a pas de fonctions de première classe. Ainsi, l'objet fonction len
a ses propres méthodes que vous pouvez inspecter en exécutant dir(len)
.Si vous n'aimez pas la façon dont cela fonctionne dans votre propre code, il est facile pour vous de réimplémenter les conteneurs en utilisant votre méthode préférée (voir exemple ci-dessous).
>>> 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
Vous pouvez également dire
>> x = 'test'
>> len(x)
4
Utilisation de Python 2.7.3.