129 votes

En Python, quand devrais-je utiliser une fonction au lieu d'une méthode?

Le Zen de Python il ya des états qui ne devrait être qu'un moyen de faire des choses - mais souvent je tombe sur le problème de la détermination de l'utilisation d'une fonction par rapport au cas d'utilisation d'une méthode.

Prenons un exemple trivial - un Échiquier de l'objet. Disons que nous avons besoin d'un moyen d'obtenir le Roi se déplace sur le plateau. Avons-nous écrire Échiquier.get_king_moves() ou get_king_moves(chess_board)?

Voici quelques questions que j'ai regardé:

Les réponses j'ai été peu concluantes:

Pourquoi Python utiliser des méthodes pour certaines fonctionnalités (par exemple, liste.index ()), mais des fonctions pour d'autres (par exemple, len(liste))?

La raison majeure est de l'histoire. Les fonctions ont été utilisées pour ces opérations, qui ont été générique pour un groupe de types et qui ont été destiné à travailler, même pour des objets qui n'ont pas du tout les méthodes de (par exemple des n-uplets). Il est aussi pratique d'avoir une fonction qui peut être facilement appliqué à un amorphe collection d'objets lorsque vous utilisez les caractéristiques fonctionnelles de Python (map(), appliquer() et al).

En fait, la mise en œuvre de len(), max(), min() comme une fonction intégrée est en fait moins de code que de les mettre en œuvre que les méthodes pour chaque type. On peut ergoter sur des cas individuels, mais c'est une partie de Python, et il est trop tard pour faire des changements fondamentaux tels maintenant. Les fonctions ont pour rester à éviter massive du code de la rupture.

Bien qu'intéressante, le ci-dessus ne veut pas vraiment dire grand-chose de ce que la stratégie à adopter.

C'est l'une des raisons - avec des méthodes personnalisées, les développeurs seraient libre de choisir un autre nom de la méthode, comme getLength(), longueur(), getlength() ou que ce soit. Python applique une stricte de nommage, de sorte que le commun de la fonction len() peut être utilisée.

Un peu plus intéressant. De mon point de vue est que les fonctions sont, en un sens, la Pythonic version d'interfaces.

Enfin, à partir de Guido lui-même:

Parler des Capacités/Interfaces m'a fait penser à certaines de nos "rogue" spécial noms de méthode. Dans la Langue de Référence, dit-il, "Un la classe peut mettre en œuvre de certaines opérations qui sont invoqués par des syntaxe (telles que des opérations arithmétiques ou subscripting et de tranchage) par la définition de méthodes ayant des noms spéciaux." Mais il y a toutes ces méthodes avec des noms spéciaux comme __len__ ou __unicode__ qui semblent être prévu pour le bénéfice de fonctions intégrées, plutôt que pour le soutien de la syntaxe. Sans doute dans une interface en Python, ces méthodes serait à son tour tenu régulièrement nommé les méthodes à l'ABC, de sorte que __len__ deviendrait

class container:
  ...
  def len(self):
    raise NotImplemented

Quoique, en y réfléchissant un peu plus, je ne vois pas pourquoi tous les syntaxiques les opérations ne serait pas juste appeler approprié normalement méthode nommée sur un ABC. "<", par exemple, aurait sans doute invoquer "object.lessthan" (ou peut-être "comparable.lessthan"). Donc, une autre avantage serait la possibilité de se sevrer de Python à l'écart de ce mutilé-nom de la singularité, ce qui me semble un HCI amélioration.

Hm. Je ne suis pas sûr que je suis d'accord (figure :-).

Il y a deux bits de "Python justification" que j'aimerais vous expliquer d'abord.

Tout d'abord, j'ai choisi len(x) sur x.len() pour HCI raisons (def __len__() sont venus beaucoup plus tard). Il y a deux entrelacés raisons en fait, les deux HCI:

(a) Pour certaines opérations, le préfixe de la notation juste se lit mieux que postfix -- prefix (et infix!) les opérations ont une longue tradition dans les mathématiques, qui aime les notations, où les images, d'aider à la mathématicien de penser à un problème. Comparer la facilité avec laquelle nous réécriture d'une formule comme x*(a+b) en x*a + x*b à la maladresse de faire la même chose à l'aide d'un raw OO notation.

(b) Quand j'ai lu le code qui, dit - len(x) je sais que c'est de demander de 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 j'ai lu x.len(), je sais déjà qu' x est une sorte de conteneur mise en œuvre d'une interface ou d'hériter d'une classe qui a une norme len(). Témoin de la confusion, nous ont de temps en temps quand une classe qui n'est pas mise en œuvre d'une cartographie a un get() ou keys() méthode, ou quelque chose qui n'est pas un fichier a un write() méthode.

Dire la même chose d'une autre manière, je vois 'len' tel que construit le fonctionnement. Je déteste perdre. Je ne peux pas dire à coup sûr si vous avez voulu ou pas, mais 'def len(self): ...' ressemble à vous souhaitez rétrograder à une méthode ordinaire. Je suis fortement -1 sur que.

Le deuxième bit de Python justification, j'ai promis à expliquer, c'est la raison pourquoi j'ai choisi une méthode spéciale pour look __special__ et non pas simplement special. Je m'attendais à beaucoup d'opérations que les classes pourrait vouloir pour remplacer, un certain niveau (par exemple, __add__ ou __getitem__), d'autres non donc standard (par exemple, les achards de l' __reduce__ pour un long temps n'avait pas de soutien dans C le code). Je n'en veux pas à ces opérations spéciales pour une utilisation ordinaire les noms de méthode, parce que les pré-existants des classes ou des classes écrit par les utilisateurs sans encyclopédique de la mémoire pour toutes les méthodes spéciales, serait de nature à accidentelle de définir des opérations qu'ils ne signifie pas pour mettre en œuvre, éventuellement avec des conséquences désastreuses. Ivan Krstić explique cette plus concis dans son message, qui est arrivé après que j'ai écrit tout cela.

-- --Guido van Rossum (page d'accueil: http://www.python.org/~guido/)

Ma compréhension de cette est que, dans certains cas, le préfixe de notation tout simplement plus de sens (c'est à dire, le Canard.quack plus de sens que quack(de Canard) à partir d'un point de vue linguistique.) et encore une fois, les fonctions de permettre des "interfaces".

Dans un tel cas, mon estimation serait de mettre en place get_king_moves basée uniquement sur Guido, premier point. Mais cela laisse encore beaucoup de questions ouvertes relatives à dire, la mise en œuvre d'une pile et file d'attente de la classe avec les mêmes méthodes push et pop - ils doivent être des fonctions ou des méthodes? (ici, je suppose que les fonctions, parce que je veux vraiment être le signal d'un push-pop de l'interface)

TLDR: quelqu'un Peut m'expliquer ce que la stratégie pour décider quand utiliser les fonctions de vs méthodes devraient être?

97voto

arrdem Points 707

Mon général, la règle est celle - ci: est l'opération effectuée sur l'objet ou par l'objet?

si c'est fait par l'objet, il doit être un membre de l'opération. S'il pouvait s'appliquer à d'autres choses aussi, ou c'est fait par quelque chose d'autre à l'objet, alors il devrait être une fonction (ou peut-être un membre de quelque chose d'autre).

Lors de l'introduction de la programmation, il est traditionnel (abet mise en œuvre incorrecte) pour décrire des objets en termes d'objets du monde réel tels que les voitures. Tu parles d'un canard, alors allons-y avec que.

class duck: 
    def __init__(self):pass
    def eat(self, o): pass 
    def crap(self) : pass
    def die(self)
    ....

Dans le contexte de la "les objets sont des choses réelles" analogie, c'est "correct" pour ajouter une méthode de classe pour quoi que ce soit dont l'objet peut faire. Donc, dis-je veux tuer un canard, puis-je ajouter un .kill() le canard? non... comme je sais que les animaux ne s'est pas suicidé. Donc si je veux tuer un canard, je dois faire ceci:

def kill(o):
    if o is duck:
        o.die()
    elif o is dog:
        print "WHY????"
        o.die()
    elif o is nyancat:
        raise Exception("NYAN "*9001)
    else:
       print "can't kill it."

Éloigner de cette analogie, pourquoi devons-nous utiliser des méthodes et des classes? Parce que nous voulons contenir des données et nous espérons que la structure de notre code, de telle manière qu'il sera réutilisable et extensible à l'avenir. Cela nous amène à la notion d'encapsulation qui est si chère à OO design.

L'encapsulation principal est vraiment ce dont il vient: en tant que concepteur, vous devriez vous cacher tout sur la mise en œuvre et les éléments internes de la classe dont il n'est absolument pas nécessairement pour un utilisateur ou un autre développeur d'accès. Parce que nous traitons avec des instances de classes, ce qui réduit de "ce que les opérations sont cruciales sur cette instance". Si une opération n'est pas l'instance spécifique, alors il ne devrait pas être une fonction membre.

LT;DR: ce que @Bryan dit. Si il fonctionne sur une instance et les besoins d'accès à des données qui est interne à l'instance de classe, il devrait être une fonction membre.

27voto

Raymond Hettinger Points 50330

Utiliser une classe si vous souhaitez:

1) Isoler le code appelant, à partir de détails de mise en œuvre -- profitant de l'abstraction et d'encapsulation.

2) Quand vous voulez être substituables par d'autres objets, profitant de polymorphisme.

3) Lorsque vous souhaitez réutiliser le code d'objets similaires -- en prenant avantage de l'héritage.

Utiliser une fonction pour les appels qui ont du sens à travers de nombreux types d'objets, par exemple, le groupe builtin len et repr fonctions s'appliquent à de nombreux types d'objets.

Cela étant dit, le choix parfois se résume à une question de goût. Penser en termes de ce qui est le plus pratique et lisible pour les appels typiques. Par exemple, ce qui serait mieux (x.sin()**2 + y.cos()**2).sqrt() ou sqrt(sin(x)**2 + cos(y)**2)?

8voto

Thriveth Points 175

J'ai l'habitude de penser à un objet, comme une personne.

Les attributs sont le nom de la personne, la hauteur, la taille de la chaussure, etc.

Les méthodes et les fonctions sont des opérations que la personne peut accomplir.

Si l'opération peut être effectuée par n'importe quel ol' personne, sans exiger quelque chose d'unique à cette une personne en particulier (et sans rien changer sur cette personne en particulier), alors c'est une fonction et doit être écrit en tant que tel.

Si une opération est en agissant sur la personne (par exemple, de manger, de marcher, ...) ou exige quelque chose d'unique à cette personne de s'impliquer (comme la danse, l'écriture d'un livre, ...), alors il devrait être une méthode.

Bien sûr, il n'est pas toujours trivial de le traduire dans l'objet spécifique que vous travaillez avec, mais je trouve que c'est une bonne façon de penser.

7voto

Bryan Oakley Points 63365

Voici une règle simple: si le code agit sur une seule instance d'un objet, utilisez une méthode. Encore mieux: utilisez une méthode à moins d’une raison impérieuse de l’écrire en tant que fonction.

Dans votre exemple spécifique, vous voulez que cela ressemble à ceci:

 chessboard = Chessboard()
...
chessboard.get_king_moves()
 

Ne réfléchis pas trop. Utilisez toujours les méthodes jusqu'au moment où vous vous dites "cela n'a pas de sens d'en faire une méthode", auquel cas vous pouvez créer une fonction.

5voto

Ben Points 22160

Généralement j'utilise des classes pour mettre en œuvre un ensemble logique de capacités pour quelque chose, de sorte que dans le reste de mon programme, je peux raisonner sur la chose, de ne pas avoir à vous soucier de toutes les petites préoccupations qui composent sa mise en œuvre.

Tout ce qui fait partie de cette de base de l'abstraction de "ce que vous pouvez faire avec une chose" sera généralement une méthode. Cela comprend généralement tout ce qui peut modifier une chose, que les données internes de l'état est généralement considéré comme privé et ne fait pas partie de la logique de l'idée de "ce que vous pouvez faire avec une chose".

Quand vous venez à la hausse du niveau des opérations, surtout si elles impliquent de multiples choses, je trouve qu'ils sont généralement plus naturellement exprimée sous forme de fonctions, si elles peuvent être construites public de l'abstraction d'une chose sans avoir besoin d'un accès spécial à l'intérieur (sauf si elles sont des méthodes d'un autre objet). Ceci a le grand avantage que lorsque je décide de réécrire complètement le fonctionnement interne de la façon dont ma chose fonctionne (sans changer l'interface), j'ai juste un petit ensemble de base des méthodes de réécriture, et puis toutes les fonctions externes écrites en termes de ces méthodes fonctionnent, tout Simplement. Je trouve que, insistant sur le fait que toutes les opérations à faire avec la classe X sont des méthodes de classe de X conduit à la sur-compliqué classes.

Il repose sur le code que j'ai écris. Pour certains programmes, j'modèle comme une collection d'objets dont les interactions donnent lieu à des le comportement du programme; ici, la plus importante de la fonctionnalité est étroitement couplée à un objet unique, et est donc mis en œuvre dans les méthodes, avec une dispersion de fonctions d'utilité. Pour les autres programmes truc le plus important est un ensemble de fonctions qui manipulent les données et les classes sont à utiliser uniquement pour mettre en œuvre le naturel "canard types" qui sont manipulées par les fonctions.

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