web-dev-qa-db-fra.com

Pourquoi le code Python utilise-t-il la fonction len () au lieu d'une méthode de longueur?

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.

Mise à jour

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.

192
fuentesjr

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

171
Jonny Buchanan

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. /… /

85

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>
37
unmounted

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) .

30
Luciano Ramalho
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.
12
Alex Coventry

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).

  • Python n’est pas un langage pur OOP - c’est un langage polyvalent à paradigmes multiples qui permet au programmeur d’utiliser le paradigme avec lequel il est le plus à l’aise et/ou le paradigme le mieux adapté à sa solution.
  • Python a des fonctions de première classe, donc 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
3
Charles Addis

Vous pouvez également dire

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

Utilisation de Python 2.7.3.

0
SpanosAngelos