150 votes

Comment dois-je concevoir une classe en Python ?

J'ai aussi eu quelques vraiment génial de l'aide sur mes questions précédentes pour détecter les pattes et les orteils à l'intérieur d'une patte, mais toutes ces solutions ne fonctionnent que pour une mesure à la fois.

Maintenant j'ai des données qui consiste à désactiver:

  • environ 30 chiens;
  • chacun a 24 mesures (divisée en plusieurs sous-groupes);
  • chaque mesure a au moins 4 contacts (un pour chaque patte) et
    • chaque contact est divisé en 5 pièces et
    • a plusieurs paramètres, comme le temps de contact, de l'emplacement, de la force totale, etc.

alt text

Évidemment coller le tout dans un gros objet ne va pas le couper, alors j'ai pensé que j'avais besoin d'utiliser des classes à la place de l'actuelle série de fonctions. Mais même si j'ai lu l'Apprentissage de Python chapitre sur les classes, je n'arrive pas à l'appliquer à mon propre code (GitHub lien)

J'ai aussi l'impression que c'est plutôt étrange de traiter toutes les données à chaque fois que je veux obtenir quelques informations. Une fois que je connais les lieux de chaque patte, il n'y a aucune raison pour moi de calculer à nouveau. En outre, je veux comparer tous les pattes du chien même pour déterminer le contact appartient à la patte (avant/arrières, gauche/droite). Ce serait un gâchis si je continue d'utiliser uniquement les fonctions.

Alors maintenant, je suis à la recherche pour obtenir des conseils sur la façon de créer des classes qui me permettra de traiter mes données (lien à la zippé données d'un chien) en un bon de la mode.

449voto

S.Lott Points 207588

Comment concevoir une classe.

  1. Écrire les mots. Vous avez commencé à le faire. Certaines personnes ne le font pas et se demandent pourquoi ils ont des problèmes.

  2. Développer votre ensemble de mots, de simples déclarations à propos de ce que ces objets seront en train de faire. C'est-à-dire, d'écrire les calculs que vous allez faire sur ces choses. Votre courte liste de 30 chiens, 24 mesures, 4 contacts, et plusieurs "paramètres" par contact est intéressant, mais seulement une partie de l'histoire. Votre "emplacement de chaque patte" et "comparer tous les pattes du chien même pour déterminer le contact appartient à la patte" sont la prochaine étape dans l'objet de la conception.

  3. Soulignez les noms. Sérieusement. Certaines personnes débattre de la valeur de cela, mais je trouve que pour la première fois OO développeurs, ça aide. Soulignez les noms.

  4. Revue les noms. Des noms génériques comme "paramètre" et "mesure" doivent être remplacés à des particuliers, des noms concrets qui s'appliquent à votre problème dans votre domaine de problème. Détails de l'aider à éclaircir le problème. Les génériques simplement éluder les détails.

  5. Pour chaque substantif ("contact", "la patte", "chien", etc.) écrire les attributs de ce nom et les actions de cet objet s'enclenche. Ne pas court-cut. Chaque attribut. "Ensemble de données contient 30 Chiens", par exemple, est important.

  6. Pour chaque attribut, identifier si c'est une relation à un nom défini, ou un autre type de "primitif" ou "atomique" de données comme une chaîne ou un float ou quelque chose d'irréductible.

  7. Pour chaque action ou d'une opération, vous devez identifier le nom a la responsabilité, et dont les noms simplement participer. C'est une question de "mutabilité". Certains objets mis à jour, d'autres ne le font pas. Mutable objets doit posséder la totale responsabilité de leurs mutations.

  8. À ce stade, vous pouvez commencer à transformer les noms dans les définitions de classe. Certains noms collectifs sont les listes, les dictionnaires, les tuples, des ensembles ou des namedtuples, et vous n'avez pas besoin de faire beaucoup de travail. D'autres classes sont plus complexes, que ce soit en raison de la complexité des données dérivées ou en raison d'une mise à jour/mutation qui est effectuée.

N'oubliez pas de tester chaque classe d'isolation à l'aide de unittest.

Aussi, il n'y a aucune loi qui dit que les classes doivent être mutables. Dans votre cas, par exemple, vous avez presque pas de données mutable. Ce que vous avez est dérivé de données, créée par les fonctions de transformation à partir de la source de données.

29voto

mitchelllc Points 519

Les conseils suivants (similaire à @S. Lott conseils) sont dans le livre, Début Python: Du Débutant au Professionnel

  1. D'écrire une description de votre problème (ce que le problème doit-il faire?). Souligner tous les noms, verbes et adjectifs.

  2. Aller à travers les noms, à la recherche de potentiels classes.

  3. Aller à travers les verbes, à la recherche de méthodes potentielles.

  4. Aller à travers les adjectifs, à la recherche de potentiels attributs

  5. Allouer des méthodes et des attributs des classes

Pour affiner la classe, le livre conseille également nous pouvons faire ce qui suit:

  1. Notez (ou le rêve) un ensemble de cas d'utilisation --- scénarios de la façon dont le programme peut être utilisé. Essayer de couvrir tous les fonctionnellement.

  2. Chaque cas d'utilisation étape par étape, faire en sorte que tout ce dont nous avons besoin est couvert.

15voto

Les Nightingill Points 375

J'aime l'approche TDD... Donc, commencer par écrire des tests pour ce que vous voulez que le comportement de l'être. Et d'écrire un code qui passe. À ce stade, ne vous inquiétez pas trop à propos de la conception, il suffit de faire une suite de test et le logiciel qui passe. Ne vous inquiétez pas si vous vous retrouvez avec un seul gros laid de classe, avec des méthodes complexes.

Parfois, au cours de ce processus initial, vous trouverez un comportement qui est difficile à tester et doit être décomposée, juste pour la testabilité. Cela peut être une indication qu'une catégorie distincte est justifiée.

Ensuite, la partie la plus amusante... refactoring. Après avoir de travail du logiciel, vous pouvez voir les pièces complexes. Souvent de petites poches de comportement deviendra apparent, ce qui suggère une nouvelle classe, mais si non, il suffit de chercher des façons de simplifier le code. Extrait d'objets de service et des objets de valeur. Simplifiez vos méthodes.

Si vous utilisez git correctement (vous utilisez git, n'est-ce pas?), vous pouvez très rapidement expérimenter avec certains de décomposition lors de refactoring, et puis de l'abandonner et de revenir en arrière si ce n'est pas simplifier les choses.

En écrivant testé code de travail d'abord, vous devez obtenir un regard intime sur le domaine du problème que vous ne pouvez pas facilement obtenir avec la conception de la première approche. L'écriture de tests de code et de vous pousser passé "où dois-je commencer" à la paralysie.

4voto

Spacedman Points 33792

L'idée de OO design est de rendre votre code de carte à votre problème, alors, quand, par exemple, vous souhaitez que le premier pas d'un chien, vous faites quelque chose comme:

dog.footstep(0)

Maintenant, il se peut que dans votre cas vous avez besoin de lire dans votre fichier raw et de calculer l'empreinte endroits. Tout cela pourrait être caché dans le sol, () la fonction de sorte qu'il se produit seulement une fois. Quelque chose comme:

 class Dog:
   def __init__(self):
     self._footsteps=None 
   def footstep(self,n):
     if not self._footsteps:
        self.readInFootsteps(...)
     return self._footsteps[n]

[C'est maintenant une sorte de mise en cache modèle. La première fois qu'il va au lit et le sol, les données, les fois suivantes il est juste de soi._footsteps.]

Mais oui, l'obtention de conception OO droit peut être difficile. Réfléchir sur les choses que vous voulez faire de vos données, et qui permettra d'informer les méthodes que vous aurez besoin d'appliquer à ce cours.

2voto

Evan Moran Points 1420

D'écrire des noms, des verbes, des adjectifs, est une bonne approche, mais je préfère penser que la classe de la conception que de poser la question de quelles données doivent être cachés?

Imaginez que vous avez un Query objet et un Database objet:

L' Query objet de vous aider à créer et stocker une requête -- magasin, est la clé ici, car une fonction peut vous aider à créer un tout aussi facilement. Peut-être que vous pourriez rester: Query().select('Country').from_table('User').where('Country == "Brazil"'). Il n'importe pas exactement la syntaxe -- c'est votre travail! - la clé est l'objet est de vous aider à cacher quelque chose, dans ce cas, les données nécessaires au stockage et à la sortie d'une requête. La puissance de l'objet vient de la syntaxe de l'utiliser (dans ce cas, certaines intelligent chaînage) et n'ayant pas besoin de savoir ce qu'il stocke pour le faire fonctionner. Si c'est bien fait l' Query objet peut en sortie de requêtes pour plus d'une base de données. En interne serait de stocker un format spécifique, mais pourrait facilement convertir vers d'autres formats lors de la sortie (Postgres, MySQL, MongoDB).

Maintenant, nous allons réfléchir à l' Database objet. Qu'est-ce cacher et de les stocker? Bien évidemment il ne peut pas stocker la totalité du contenu de la base de données, depuis c'est pourquoi nous avons une base de données! Alors quel est le point? Le but est de cacher la façon dont la base de données fonctionne de personnes qui utilisent l' Database objet. Bon les classes de simplifier le raisonnement lors de la manipulation de l'état interne. Pour cette Database objet, vous pouvez le cacher comment la mise en réseau des appels, ou un lot de requêtes ou de mises à jour, ou de fournir une couche de mise en cache.

Le problème, c'est ce Database objet est ÉNORME. Il représente l'accès à une base de données, sous les couvertures, il pourrait faire tout et n'importe quoi. Clairement mise en réseau, la mise en cache, et le dosage sont très difficiles à traiter en fonction de votre système, afin de les cacher loin serait très utile. Mais, comme beaucoup de gens remarque, une base de données est incroyablement complexe, et la poursuite de la crue DB appels que vous obtenez, plus il est difficile d'optimisation de performances et de comprendre comment les choses fonctionnent.

C'est la base de compromis de la programmation orientée objet. Si vous choisissez le bon abstraction il rend la programmation plus simple (String, Array, Dictionnaire), si vous choisissez une abstraction qui est trop gros (Base de données, EmailManager, NetworkingManager), il peut devenir trop complexe pour vraiment comprendre comment il fonctionne, ou à quoi s'attendre. Le but est de masquer la complexité, mais une certaine complexité est nécessaire. Une bonne règle de base est de commencer en évitant Manager objets, et, au lieu de créer des classes qui sont comme structs -- tout ce qu'ils font est de maintenir des données, avec certaines méthodes d'aide à la création/manipuler les données pour rendre votre vie plus facile. Par exemple, dans le cas d' EmailManager commencer avec une fonction appelée sendEmail qui prend un Email objet. C'est un point de départ simple et le code est très simple à comprendre.

Quant à votre exemple, pensez à ce que les données doivent être ensemble pour calculer ce que vous cherchez. Si vous avez voulu savoir dans quelle mesure un animal était en train de marcher, par exemple, vous pourriez avoir AnimalStep et AnimalTrip (collection de AnimalSteps) des classes. Maintenant que chaque Voyage a tous les Étape de données, alors il devrait être en mesure de comprendre des choses sur elle, peut - AnimalTrip.calculateDistance() de sens.

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