9
votes

Les avantages d'avoir une fonction statique comme Len (), max () et Min () sur des appels de méthode hérités

Je suis un débutant python, et je ne suis pas sûr de savoir pourquoi Python implémentée Len (Obj), Max (Obj) et Min (OBJ) en tant que fonctions statiques comme (je viens de la langue java) sur obj.len ( ), obj.max () et obj.min ()

Quels sont les avantages et les inconvénients (autres que l'incohérence évidente) d'avoir LEN () ... sur les appels de méthode?

Pourquoi Guido a-t-il choisi cela sur les appels de méthode? (Cela aurait pu être résolu dans Python3 si nécessaire, mais cela n'a pas été changé dans Python3, il faut donc que ce soit de bonnes raisons ... j'espère)

merci !!


2 commentaires

Je trouve personnellement min (1,2) et min ([1,2]) extrêmement cohérent. Tout ne devrait pas être java comme


@Tomleys: Comment len (x) cohérent avec des appels de méthode tels que x.foo () ? Ils sont tous les deux opérations sur l'objet X, n'est-ce pas? Il ne s'agit pas d'être comme de java. Il s'agit de se conformer au Principe de la moins surprise . Ruby suit ce principe.


4 Réponses :


3
votes

Il met l'accent sur les capacités d'un objet, non de ses méthodes ou de son type. Les capabilites sont déclarées par des fonctions "Helper" telles que __ iTer __ et __ len __ mais ils ne constituent pas l'interface. L'interface est dans les fonctions intégrées et à côté de cela également dans les opérateurs de buit-in, tels que + et [] pour l'indexation et la tranchée.

Parfois, ce n'est pas une correspondance unique: par exemple, iter (obj) renvoie un itérateur pour un objet et fonctionnera même si __ iter __ n'est pas défini. S'il n'est pas défini, il se passe pour rechercher si l'objet définit __ getItem __ et retournera un itérateur accédant à l'index d'objet-wise (comme un tableau).

Cela va avec le dactylographie du canard de Python, nous ne nous soucions que de ce que nous pouvons faire avec un objet, pas qu'il s'agisse d'un type particulier.


0 commentaires

3
votes

En fait, ceux-ci ne sont pas des méthodes "statiques" dans la façon dont vous pensez à eux. Ils sont Fonctions intégrées qui vient d'alias de certaines méthodes sur des objets Python qui implémentent eux.

>>> class Foo(object):
...     def __len__(self):
...             return 42
... 
>>> f = Foo()
>>> len(f)
42


3 commentaires

Non, c'est juste Len. Il n'y a pas de __ min __ ou __ max __ méthodes spéciales.


Le but est que la question les traite tous semblable, ce qui est incorrect. Cette réponse dit - correctement - que ce n'est pas un motif général, mais il y a un mélange de choses avec une notation commune.


Même f .____ len __ () n'est pas un alias pour len (f) . Si votre __ len __ La méthode spéciale renvoie quelque chose de non entier (ou même un grand nombre d'entiers), la fonction len échouera avec un typeError .



19
votes

Le grand avantage est que les fonctions intégrées (et les opérateurs) peuvent appliquer une logique supplémentaire le cas échéant, au-delà d'appeler simplement les méthodes spéciales. Par exemple, min peut examiner plusieurs arguments et appliquer les contrôles d'inégalité appropriés, ou peut accepter un seul argument itérable et poursuivre de la même manière; ABS Lorsque vous appuyez sur un objet sans méthode spéciale __ abs __ pourrait essayer de comparer ledit objet avec 0 et à l'aide de la méthode de signe de changement d'objet si nécessaire (bien qu'il ne soit pas actuellement); et ainsi de suite.

Donc, pour la cohérence, toutes les opérations avec une large applicabilité doivent toujours passer à travers des bâtiments et / ou des opérateurs, et il est de la responsabilité intégrée à rechercher et à appliquer les méthodes spéciales appropriées (sur un ou plusieurs des arguments). , utilisez une logique alternative le cas échéant, et ainsi de suite.

Un exemple où ce principe n'a pas été appliqué correctement (mais l'incohérence a été corrigée dans Python 3) est "Étape un itérateur vers l'avant": en 2,5 et antérieure, vous devez définir et appeler le Suivant méthode sur l'itérateur. En 2.6 et plus tard, vous pouvez le faire de la bonne façon: l'objet itérateur définit __ suivant __ , le nouveau suivant intégré peut appeler et appliquer Logique supplémentaire, par exemple pour alimenter une valeur par défaut (en 2.6, vous pouvez toujours le faire de la mauvaise manière ancienne, pour la compatibilité en arrière, bien que dans 3. * Vous ne pouvez plus).

Un autre exemple: Considérez l'expression x + y . Dans une langue traditionnelle orientée objet (capable d'expédier uniquement sur le type de l'argument le plus à gauche - comme Python, Ruby, Java, C ++, C #, C) si X est de type intégré et y est de votre propre nouveau type de fantaisie, vous avez malheureusement mal de chance si la langue insiste sur la délégation de la logique à la méthode de type (x) qui implémente Ajout (en supposant que la langue permet une surcharge de l'opérateur; -).

en python, le + (et bien sûr bien sûr le coastine opérateur.add , si c'est ce que vous préférez) essaie __ d'ajouter __ ajouter __ , et si cela ne sait pas quoi faire avec y , puis essaie __ radd __ de type __ . Vous pouvez donc définir vos types qui savent comment s'ajouter aux entiers, aux flotteurs, au complexe, etc., etc., ainsi que ceux qui savent ajouter de tels types numériques intégrés à eux-mêmes (c'est-à-dire que vous pouvez le coder pour que x + y et y + x fonctionne bien, lorsque y est une instance de votre nouveau type de fantaisie et x est une instance de type numérique intégré).

"Fonctions génériques" (comme en pic) est une approche plus élégante (permettant de laisser tout premier plan basé sur une combinaison de types, jamais avec la focale de monomane fou sur les arguments les plus grands que OOP encourage! - ), mais (a) ils n'étaient malheureusement pas acceptés pour Python 3, et (b) qu'ils font bien sûr exigent que la fonction générique soit exprimée comme libre (il serait absolument fou de devoir envisager la fonction "appartenance" à n'importe quel type, où le point entier est que cela peut être différent de substitution / surchargé en fonction de la combinaison arbitraire de ses types d'arguments! -). Toute personne qui a toujours été programmée dans les Lisp, Dylan ou Peak, sait de quoi je parle; -).

Ainsi, les fonctions autonomes et les opérateurs sont simplement le moyen droit et cohérent d'aller (même si le manque de fonctions génériques, dans les os nus python, supprimez une fraction de l'élégance inhérente C'est toujours un mélange raisonnable d'élégance et de praticité! -).


6 commentaires

Vous dites "Capable d'expédier uniquement sur le type de l'argument le plus à gauche - comme Python, C ++, ...", puis montrez que Python peut également être envoyé sur le bon argument avec __ radd __ . En outre, C ++ peut envoyer des deux arguments si vous déclarez opérateur + comme fonction libre. Cela semble être un peu incompatible ...


@sth, python ne peut pas Dispatch sur l'argument RHS - il peut utiliser un intégré ou un opérateur pour faire le travail grogneur. En C ++, si opérateur + est une fonction autonome, elle peut être surchargée en fonction des types visibles statilement visibles (AKA Compile-Time-Time Visible) des opérandes, il suffit de < b> ne peut pas Dispatch , c'est-à-dire que vous utilisez strictement les types d'opérandes Runtime pour choisir le code à exécuter (en fonction de la fonction de membre, mais uniquement sur le côté gauche de gauche type). Rien d'incohérent, pour les lecteurs qui sait ce que signifie envoi, et la différence entre les types de temps de compilée et de temps d'exécution! -)


Bonne explication, mais elle n'est pas applicable à len () - la décision de conception la plus discutable.


@Denis, l'uniformité est une force de conception linguistique: il serait totalement absurde si, pour chaque opération applicable à une vaste catégorie d'objets, vous deviez mémoriser "bien voyons-la, est-ce une fonction intégrée comme la plupart, ou est-ce une des exceptions "( suivant en 2.5 et antérieure a été la mauvaise décision de conception, comme je l'ai mentionné, définitivement pas len ). "Aucune exception à cette règle de conception" est clairement la décision de conception ici, pas des dizaines de disparates, une par chaque charcuterie de fonctionnalité!


Fonctions UIL-IN VS Méthodes héritées. --- pour LEN (), je pense que Leng () aurait pu être mieux comme une méthode héritée, mais après avoir lu la FAQ de Google GO LICKING, la décision était peut-être fondée sur la mise en œuvre facile ------> de golang.org -------> Nous avons débattu de ce problème, mais j'ai décidé que la mise en œuvre de Len et des amis en tant que fonctions était bien en pratique et n'a pas compliqué les questions concernant l'interface (dans le sens du type Go) des types de base -


@Denisotkidach Il y a une très bonne justification par Armin Ronacher sur Len () . Mettez simplement, il applique la cohérence dans les nombreux noms de «get de la longueur» potentiellement. Dans d'autres langues, vous devrez peut-être rechercher si vous devez utiliser x.length ou x.size ou même x.getsize () , etc. en python, c'est toujours len (x) . lucumr.pocoo.org/2011/7/9/python-et -pola



1
votes

Je pensais que la raison était que ces opérations de base pourraient être effectuées sur des itérateurs avec la même interface que les conteneurs. Cependant, cela ne fonctionne pas avec LEN:

def add(f):
    f(1)
    f(2)
    f(3)
lst = []
add(lst.append)
print lst


0 commentaires