web-dev-qa-db-fra.com

Classes vs Fonctions

Les fonctions sont faciles à comprendre, même pour quelqu'un qui n'a aucune expérience en programmation, mais qui possède de bonnes connaissances en mathématiques. En revanche, les cours semblent plus difficiles à appréhender.

Supposons que je souhaite créer une classe/fonction qui calcule l'âge d'une personne en fonction de son année de naissance et de l'année en cours. Devrais-je créer une classe pour cela, ou une fonction? Ou le choix dépend-il du scénario?

P.S. Je travaille sur Python, mais je suppose que la question est générique.

52
multigoodverse

Créer une fonction. Fonctions do choses spécifiques, classes sont choses spécifiques.

Les classes ont souvent des méthodes, qui sont des fonctions associées à une classe particulière, et font des choses associées à la chose qui est la classe - mais si tout ce que vous voulez, c'est faire quelque chose, une fonction est tout ce dont vous avez besoin.

Une classe est essentiellement un moyen de regrouper des fonctions (en tant que méthodes) et des données (en tant que propriétés) dans une unité logique tournant autour d'un certain type de choses. Si vous n'avez pas besoin de ce groupe, vous n'avez pas besoin de faire un cours.

92
Amber

Comme quoi Amber dit dans sa réponse : créer une fonction. En fait, quand vous n'avez pas devez faire des cours si vous avez quelque chose comme:

class Person(object):
    def __init__(self, arg1, arg2):
        self.arg1 = arg1
        self.arg2 = arg2

    def compute(other):
        """ Example of bad class design, don't care about the result """
        return self.arg1 + self.arg2 % other

Ici, vous avez juste une fonction encapsuler dans une classe. Cela rend simplement le code moins lisible et moins efficace. En fait, la fonction compute peut être écrite comme ceci:

def compute(arg1, arg2, other):
     return arg1 + arg2 % other

Vous ne devriez utiliser les classes que si vous avez plus d'une fonction et si conserver un état interne (avec attributs) a du sens. Sinon, si vous souhaitez regrouper des fonctions, créez simplement un module dans un nouveau fichier .py fichier.

Vous pouvez regarder cette vidéo (Youtube, environ 30min) , ce qui explique mon propos. Jack Diederich montre pourquoi les classes sont diaboliques dans ce cas et pourquoi leur conception est si mauvaise, en particulier dans les cas comme l'API.
C'est assez long mais c'est à voir absolument

15
Maxime Lorant

Les classes (ou plutôt leurs instances) servent à représenter des choses. Les classes permettent de définir les opérations prises en charge par une classe d'objets particulière (ses instances). Si votre application doit garder une trace des personnes, alors Person est probablement une classe; les instances de cette classe représentent des personnes particulières que vous suivez.

Les fonctions sont pour calculer des choses. Ils reçoivent des entrées et produisent une sortie et/ou ont des effets.

Les classes et les fonctions ne sont pas vraiment des alternatives, car elles ne servent pas les mêmes choses. Envisager de créer un cours pour "calculer l'âge d'une personne en fonction de son année d'anniversaire et de l'année en cours" n'a pas de sens. Vous pouvez ou non avoir des classes représentant l'un des concepts de Person, Age, Year et/ou Birthday. Mais même si Age est une classe, il ne faut pas penser à calculer l’âge d’une personne; plutôt le calcul de l'âge d'une personne résultats dans une instance de la classe Age.

Si vous modélisez des personnes dans votre application et que vous avez une classe Person, il peut être judicieux de faire en sorte que le calcul de l'âge soit une méthode de la classe Person. Une méthode est fondamentalement une fonction définie comme faisant partie d'une classe; C'est ainsi que vous "définissez les opérations supportées par une classe d'objets particulière", comme je l'ai mentionné précédemment.

Vous pouvez donc créer une méthode sur votre classe de personnes pour calculer l'âge de la personne (elle récupérera probablement l'année d'anniversaire de l'objet Personne et recevra l'année en cours en tant que paramètre). Mais le calcul est toujours effectué par une fonction (juste une fonction qui s'avère être une méthode sur une classe).

Vous pouvez également créer une fonction autonome qui reçoit des arguments (un objet personne à partir duquel récupérer une année de naissance ou simplement l'année de naissance elle-même). Comme vous le constatez, ceci est beaucoup plus simple si vous n'avez pas déjà une classe où cette méthode appartient naturellement! Vous ne devriez jamais créer une classe simplement pour tenir une opération; si c'est tout il y en a pour la classe, alors l'opération devrait simplement être une fonction autonome.

6
Ben

Cela dépend du scénario. Si vous niquement quoi calculer l'âge d'une personne, utilisez une fonction puisque vous souhaitez implémenter un comportement spécifique nique.

Mais si vous souhaitez créer un objet contenant la date de naissance d’une personne (et éventuellement d’autres données), permet de le modifier, le calcul de l’âge pourrait alors être l’une des nombreuses opérations liées à la personne et il serait judicieux de: utilisez plutôt une classe.

Les classes permettent de fusionner certaines données et opérations associées. Si vous n'avez qu'une seule opération sur les données, alors en utilisant une fonction et en passant les données en argument, vous obtenez un comportement équivalent, avec un code moins complexe.

Notez qu'une classe du genre:

class A(object):
    def __init__(self, ...):
        #initialize
    def a_single_method(self, ...):
        #do stuff

ce n'est pas vraiment une classe, ce n'est qu'une fonction (compliquée). Une classe légitime doit toujours avoir au moins deux méthodes (sans compter __init__).

5
Bakuriu

je sais que c'est un sujet controversé et que je suis probablement brûlé maintenant. mais voici mes pensées.

Pour ma part, j’ai pensé qu’il était préférable d’éviter les cours le plus longtemps possible. Si j'ai besoin d'un type de données complexe, j'utilise une structure simple (C/C++), dict (python), JSON (js) ou similaire, c'est-à-dire pas de constructeur, pas de méthode de classe, pas de surcharge d'opérateur, pas d'héritage, etc. vous pouvez vous laisser emporter par OOP lui-même (modèle de conception, ce qui devrait être privé, bla bla)) et perdre le focus sur l'essentiel que vous vouliez coder.

Si votre projet devient volumineux et désordonné, alors OOP commence à avoir un sens, car une sorte d'architecture de système de visualisation par hélicoptère est nécessaire. "Function vs class" dépend également de la tâche qui vous attend.

une fonction

  • but: traiter des données, manipuler des données, créer des ensembles de résultats.
  • quand utiliser: toujours coder une fonction si vous voulez faire ceci: “y = f (x)”

struct/dict/json/etc (au lieu de classe)

  • but: stocker attr./param., maintenir attr./param., réutiliser attr./param., utiliser attr./param. plus tard.
  • quand utiliser: si vous traitez avec un ensemble d'attributs/paramètres (de préférence non modifiable)
  • différents langages même chose: struct (C/C++), JSON (js), dict (python), etc.
  • préférez toujours les simples struct/dict/json/etc aux classes compliquées (restez simple!)

classe (s'il s'agit d'un nouveau type de données)

  • une perspective simple: est une structure (C), dict (python), json (js), etc. avec les méthodes attachées.
  • La méthode ne devrait avoir de sens qu'en combinaison avec les données/paramètres stockés dans la classe.
  • mon conseil: ne codez jamais de contenu complexe dans les méthodes de classe (appelez plutôt une fonction externe)
  • attention: n'utilisez pas les classes comme faux espace de noms pour des fonctions! (cela arrive très souvent!)
  • autres cas d'utilisation: si vous souhaitez effectuer beaucoup de surcharge d'opérateur, utilisez des classes (par exemple, votre propre classe de multiplication matrice/vecteur)
  • demandez-vous: s'agit-il vraiment d'un nouveau "type de données"? (Yes => class | No => pouvez-vous éviter d'utiliser une classe)

tableau/vecteur/liste (pour stocker beaucoup de données)

  • but: stocker beaucoup de données homogènes du même type de données, par ex. des séries chronologiques
  • conseil n ° 1: utilisez simplement ce que votre langage de programmation possède déjà. ne pas le réinventer
  • conseil n ° 2: si vous voulez vraiment votre “classe mysupercooldatacontainer”, surchargez un tableau/vecteur/liste/etc existant (par exemple “classe mycontainer: public std :: vector…”)

enum (classe enum)

  • je viens de le mentionner
  • conseil n ° 1: utilisez enum plus switch-case au lieu de trop compliqué OOP modèles de conception
  • conseil n ° 2: utiliser des machines à états finis
4
ulf

Avant de répondre à votre question:

Si vous n'avez pas de classe Person, vous devez d'abord déterminer si vous souhaitez créer une classe Person. Envisagez-vous de réutiliser très souvent le concept de Person? Si c'est le cas, vous devriez créer une classe Person. (Vous avez accès à ces données sous la forme d'une variable transmise et vous ne vous souciez pas d'être désordonné et négligé.)

Pour répondre à ta question:

Vous avez accès à leur année de naissance. Dans ce cas, vous avez probablement une classe Person avec un someperson.birthdate champ. Dans ce cas, vous devez vous demander si someperson.age une valeur réutilisable?

La réponse est oui. Nous nous soucions souvent plus de l’âge que de la date de naissance, donc si la date de naissance est un champ, l’âge doit être un champ dérivé. (Un cas où nous ne ferions pas ceci: si nous calculions des valeurs comme someperson.chanceIsFemale ou someperson.positionToDisplayInGrid ou d’autres valeurs non pertinentes, nous n’allons pas étendre la classe Person; vous vous demandez simplement: "Est-ce qu'un autre programme s'intéresserait aux domaines dans lesquels je pense étendre la classe?" La réponse à cette question déterminera si vous élargissez la classe d'origine ou créez une fonction (ou votre propre classe comme PersonAnalysisData ou quelque chose du genre).)

3
ninjagecko

Ne créez jamais de classes. Au moins le type de classes OOP dans Python est en cours de discussion).

Considérez cette classe simpliste:

class Person(object):
    def __init__(self, id, name, city, account_balance):
        self.id = id
        self.name = name
        self.city = city
        self.account_balance = account_balance

    def adjust_balance(self, offset):
        self.account_balance += offset


if __== "__main__":
    p = Person(123, "bob", "boston", 100.0)
    p.adjust_balance(50.0)
    print("done!: {}".format(p.__dict__))

vs cette version nommée:

from collections import namedtuple

Person = namedtuple("Person", ["id", "name", "city", "account_balance"])


def adjust_balance(person, offset):
    return person._replace(account_balance=person.account_balance + offset)


if __== "__main__":
    p = Person(123, "bob", "boston", 100.0)
    p = adjust_balance(p, 50.0)
    print("done!: {}".format(p))

L'approche namedtuple est préférable car:

  • namedtuples ont une syntaxe plus concise et une utilisation standard.
  • En termes de compréhension du code existant, les tubes nommés sont faciles à comprendre. Les cours sont plus complexes. Et les classes peuvent devenir très complexes pour les humains.
  • les tubes nommés sont immuables. La gestion de l'état mutable ajoute une complexité inutile.
  • la classe inheritance ajoute de la complexité et masque la complexité.

Je ne vois aucun avantage à utiliser les classes OOP. Évidemment, si vous êtes habitué à la POO, ou si vous devez vous interfacer avec du code nécessitant des classes telles que Django.

En passant, la plupart des autres langues ont une fonctionnalité de type d’enregistrement telle que namedtuples. Scala, par exemple, a des classes de cas. Cette logique s'applique également là.

3
clay

Je vais rompre avec le troupeau et donner un autre point de vue:

Ne créez jamais de classes.

Le recours à des classes a fortement tendance à amener les codeurs à créer du code saturé et lent. Les classes qui sont passées (car ce sont des objets) demandent beaucoup plus de temps de calcul que d'appeler une fonction et de passer une chaîne ou deux. Des conventions de nommage appropriées sur les fonctions peuvent faire à peu près tout ce que peut créer une classe, avec seulement une fraction de la surcharge et une meilleure lisibilité du code.

Cela ne signifie pas pour autant que vous ne devriez pas apprendre à comprendre les cours. Si vous codez avec d'autres personnes, les gens les utiliseront tout le temps et vous devrez savoir comment jongler avec ces classes. Écrire votre code pour utiliser des fonctions signifie qu'il sera plus petit, plus rapide et plus lisible. J'ai vu d'énormes sites écrits en n'utilisant que des fonctions rapides et rapides, et de minuscules sites dotés de fonctionnalités minimales, qui reposaient sur des classes et se rompaient constamment. (Lorsque vous avez des classes prolongeant des classes contenant des classes dans le cadre de leurs classes, vous savez que vous avez perdu tout semblant de facilité de maintenance.)

En fin de compte, toutes les données que vous voulez transmettre peuvent être facilement gérées par les types de données existants.

Les classes ont été créées comme une béquille mentale et ne fournissent aucune fonctionnalité supplémentaire réelle , et le code trop compliqué qu'elles ont tendance à créer fait échouer le point de cette béquille à long terme.

2
liljoshu