143 votes

Différence entre len() et .__len__() ?

Y a-t-il une différence entre appeler len([1,2,3]) o [1,2,3].__len__() ?

S'il n'y a pas de différence visible, qu'est-ce qui est fait différemment en coulisses ?

3 votes

143voto

Mike Graham Points 22480

len est une fonction permettant d'obtenir la longueur d'une collection. Elle fonctionne en appelant la fonction __len__ méthode. __something__ Les attributs sont spéciaux et généralement plus nombreux qu'il n'y paraît, et ne doivent généralement pas être appelés directement.

Il y a longtemps, il a été décidé que la longueur d'un élément devait être une fonction et non un code de méthode, car le raisonnement était le suivant len(a) Le sens de l'expression serait clair pour les débutants mais a.len() ne serait pas aussi claire. Lorsque Python a commencé __len__ n'existait même pas et len était une chose spéciale qui fonctionnait avec quelques types d'objets. Que la situation dans laquelle nous nous trouvons soit logique ou non, elle est là pour rester.

0 votes

Je dirais qu'avoir un standard universel dans tout le langage pour obtenir la longueur d'un objet, quelle que soit la structure de données, est plutôt génial. C'est agréable de ne pas avoir à se rappeler si c'est obj.length, obj.length(), obj.size, obj.size(), ou n'importe quoi d'autre qui a été imaginé au fil des ans

86voto

Alex Martelli Points 330805

Il arrive souvent que le comportement "typique" d'un intégré ou d'un opérateur consiste à appeler (avec une syntaxe différente et plus agréable) des méthodes magiques appropriées (dont les noms sont du type __whatever__ ) sur les objets concernés. Souvent, l'intégré ou l'opérateur a une "valeur ajoutée" (il est capable de prendre des chemins différents selon les objets impliqués) -- dans le cas de len vs __len__ Il s'agit simplement d'une vérification de l'intégrité de l'intégration qui manque à la méthode magique :

>>> class bah(object):
...   def __len__(self): return "an inch"
... 
>>> bah().__len__()
'an inch'
>>> len(bah())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object cannot be interpreted as an integer

Lorsque vous voyez un appel à la len intégré, vous êtes sûr que, si le programme continue après cela plutôt que de lever une exception, l'appel a renvoyé un nombre entier, non négatif, et <= sys.maxsize -- quand vous voyez un appel à xxx.__len__() vous n'avez aucune certitude (si ce n'est que l'auteur du code ne connaît pas bien Python ou qu'il ne fait rien de bien ;-).

D'autres modules intégrés offrent une valeur ajoutée allant au-delà des simples contrôles d'intégrité et de lisibilité. En concevant uniformément tout Python pour qu'il fonctionne par le biais d'appels aux builtins et d'utilisation d'opérateurs, et jamais par le biais d'appels à des méthodes magiques, les programmeurs n'ont pas à se rappeler quel cas est le bon. (Parfois, une erreur se glisse : jusqu'à la version 2.5, il fallait appeler foo.next() -- en 2.6, bien que cela fonctionne toujours pour la compatibilité ascendante, vous devriez appeler next(foo) et dans 3.* la méthode magique est correctement nommée __next__ au lieu du "oops-ey" next !-).

La règle générale devrait donc être de ne jamais appeler une méthode magique directement (mais toujours indirectement par le biais d'une méthode intégrée) à moins que vous ne sachiez exactement pourquoi vous devez le faire (par exemple, lorsque vous surchargez une telle méthode dans une sous-classe, si la sous-classe doit s'en remettre à la super-classe, cela doit être fait par un appel explicite à la méthode magique).

0 votes

Je suis un utilisateur débutant de Python (pas le programmeur débutant pensé) et je ne suis pas sûr de "Quand vous voyez un appel à l'intégré len, vous êtes sûr que, si le programme continue après cela plutôt que de lever une exception". J'ai essayé ceci : def len(x): return "I am a string." print(len(42)) print(len([1,2,3])) et il a imprimé I am string deux fois. Pouvez-vous l'expliquer davantage ?

4 votes

@DarekNedza Cela n'a rien à voir avec ce qui précède, qui concerne les len intégrés. Vous venez de définir votre fonction len, qui peut bien sûr retourner ce que vous voulez. L'OP parle de builtin len, qui appelle __len__ spécial méthode (pas de fonction) sur l'objet considéré.

0 votes

@Veky Comment puis-je être sûr que j'appelle la fonction intégrée len et non pas une autre fonction (comme dans mon exemple) qui se trouve avoir le même nom - len . Il n'y a pas d'avertissement comme "Vous redéfinissez la fonction intégrée len" ou quelque chose comme ça. À mon avis, je ne peux pas être sûr de ce qu'Alex a déclaré dans sa réponse.

32voto

gnibbler Points 103484

Vous pouvez penser que len() est à peu près équivalent à

def len(x):
    return x.__len__()

L'un des avantages est qu'il vous permet d'écrire des choses comme

somelist = [[1], [2, 3], [4, 5, 6]]
map(len, somelist) 

au lieu de

map(list.__len__, somelist)

ou

map(operator.methodcaller('__len__'), somelist)

Il y a cependant un comportement légèrement différent. Par exemple, dans le cas des ints

>>> (1).__len__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__len__'
>>> len(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()

2 votes

Je suppose que vous voulez dire operator.methodcaller au lieu de operator.attrgetter .

5voto

Dmytro Ozarkiv Points 91

Vous pouvez vérifier Documentation sur Pythond :

>>> class Meta(type):
...    def __getattribute__(*args):
...       print "Metaclass getattribute invoked"
...       return type.__getattribute__(*args)
...
>>> class C(object):
...     __metaclass__ = Meta
...     def __len__(self):
...         return 10
...     def __getattribute__(*args):
...         print "Class getattribute invoked"
...         return object.__getattribute__(*args)
...
>>> c = C()
>>> c.__len__()                 # Explicit lookup via instance
Class getattribute invoked
10
>>> type(c).__len__(c)          # Explicit lookup via type
Metaclass getattribute invoked
10
>>> len(c)                      # Implicit lookup
10

2voto

Ankit Kr Points 1

Bien, len(s) est une méthode Python intégrée qui renvoie la longueur d'un objet. Maintenant __len__() est une méthode spéciale qui est appelée en interne par len(s) pour retourner la longueur d'un objet.

Donc, quand nous appelons len(s) méthode, s.__len__() est ce qui se passe réellement en coulisses pour calculer la longueur.

Le Python len() peut être interprétée comme :

def len(s):
    return s.__len__()

référence

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X