web-dev-qa-db-fra.com

Quand devrais-je utiliser des classes en Python?

Je programme en python depuis environ deux ans; principalement des données (pandas, mpl, numpy), mais aussi des scripts d'automatisation et de petites applications Web. J'essaie de devenir un meilleur programmeur et d’augmenter mes connaissances python. L’un des problèmes qui me dérange, c’est que je n’ai jamais utilisé de classe (en dehors de la copie aléatoire flask code pour petites applications web). Je comprends généralement ce qu’ils sont, mais je n'arrive pas à comprendre pourquoi j’en aurais besoin d’une simple fonction.

Pour ajouter de la spécificité à ma question: j’écris des tonnes de rapports automatisés qui impliquent toujours d’extraire des données de multiples sources de données (mongo, sql, postgres, apis), d’effectuer un grand nombre ou un peu de modifications et de formatages de données, de les écrire sur csv/Excel/html, envoyez-le dans un email. Les scripts vont de ~ 250 lignes à ~ 600 lignes. Y aurait-il une raison pour que j'utilise les classes pour faire cela et pourquoi?

101
metersk

Les classes sont le pilier de Programmation Orientée Objet . OOP est très préoccupé par l'organisation du code, sa réutilisabilité et son encapsulation.

Premièrement, une clause de non-responsabilité: OOP est partiellement opposée à programmation fonctionnelle , qui est un paradigme différent utilisé fréquemment en Python. Tous ceux qui programment en Python (ou sûrement la plupart des langages) n'utilisent pas la POO. Vous pouvez faire beaucoup dans Java 8 qui n'est pas très orienté objet. Si vous ne voulez pas utiliser la POO, alors ne le faites pas. Si vous écrivez simplement des scripts uniques pour traiter des données que vous n'utiliserez plus jamais, continuez à écrire comme vous êtes.

Cependant, il y a beaucoup de raisons d'utiliser la POO.

Certaines raisons:

  • Organisation: OOP définit des méthodes bien connues et standard de description et de définition des données et des procédures dans le code. Les données et les procédures peuvent être stockées à différents niveaux de définition (dans différentes classes), et il existe des méthodes standard pour parler de ces définitions. Autrement dit, si vous utilisez OOP de manière standard, cela aidera votre futur utilisateur et les autres à comprendre, modifier et utiliser votre code. De plus, au lieu d’utiliser un mécanisme complexe et arbitraire de stockage de données (dict de dict ou listes ou dict ou de dict d’ensembles, ou autre), vous pouvez nommer des éléments de structures de données et les référencer facilement.

  • État: OOP vous aide à définir et à suivre l'état. Par exemple, dans un exemple classique, si vous créez un programme qui traite les étudiants (par exemple, un programme de notes), vous pouvez conserver toutes les informations dont vous avez besoin à leur sujet au même endroit (nom, âge, sexe, niveau scolaire, etc.). cours, notes, professeurs, pairs, régime alimentaire, besoins spéciaux, etc.), et ces données sont conservées tant que l'objet est en vie et facilement accessible.

  • Encapsulation : Avec l'encapsulation, la procédure et les données sont stockées ensemble. Les méthodes (un OOP terme pour des fonctions) sont définies juste à côté des données sur lesquelles elles fonctionnent et sont produites. Dans un langage tel que Java qui autorise contrôle d'accès , ou en Python, selon la manière dont vous décrivez votre API publique, cela signifie que les méthodes et les données peuvent être masquées à l'utilisateur. Cela signifie que si vous avez besoin ou souhaitez modifier le code, vous pouvez faire ce que vous voulez pour l'implémentation du code, tout en conservant les API publiques.

  • Héritage : l'héritage vous permet de définir des données et une procédure à un endroit (dans une classe), puis de remplacer ou d'étendre cette fonctionnalité ultérieurement. Par exemple, en Python, je vois souvent des personnes créer des sous-classes de la classe dict afin d'ajouter des fonctionnalités supplémentaires. Un changement courant remplace la méthode qui lève une exception lorsqu'une clé est demandée à un dictionnaire qui n'existe pas pour donner une valeur par défaut basée sur une clé inconnue. Cela vous permet d'étendre votre propre code maintenant ou plus tard, d'autoriser d'autres personnes à étendre votre code et vous permet d'étendre le code d'autres personnes.

  • Réutilisation: Toutes ces raisons et d'autres permettent une plus grande réutilisabilité du code. Le code orienté objet vous permet d'écrire du code solide (testé) une fois, puis de le réutiliser encore et encore. Si vous devez ajuster quelque chose pour votre cas d'utilisation spécifique, vous pouvez hériter d'une classe existante et écraser le comportement existant. Si vous devez changer quelque chose, vous pouvez tout changer en conservant les signatures de méthode publique existantes, et personne n'est le plus sage (espérons-le).

Encore une fois, il y a plusieurs raisons de ne pas utiliser la POO, et vous n'en avez pas besoin. Mais heureusement, avec un langage comme Python, vous pouvez en utiliser un peu ou beaucoup, à vous de choisir.

Un exemple du cas d'utilisation de l'étudiant (pas de garantie sur la qualité du code, juste un exemple):

Orienté Objet

class Student(object):
    def __init__(self, name, age, gender, level, grades=None):
        self.name = name
        self.age = age
        self.gender = gender
        self.level = level
        self.grades = grades or {}

    def setGrade(self, course, grade):
        self.grades[course] = grade

    def getGrade(self, course):
        return self.grades[course]

    def getGPA(self):
        return sum(self.grades.values())/len(self.grades)

# Define some students
john = Student("John", 12, "male", 6, {"math":3.3})
jane = Student("Jane", 12, "female", 6, {"math":3.5})

# Now we can get to the grades easily
print(john.getGPA())
print(jane.getGPA())

Dict standard

def calculateGPA(gradeDict):
    return sum(gradeDict.values())/len(gradeDict)

students = {}
# We can set the keys to variables so we might minimize typos
name, age, gender, level, grades = "name", "age", "gender", "level", "grades"
john, jane = "john", "jane"
math = "math"
students[john] = {}
students[john][age] = 12
students[john][gender] = "male"
students[john][level] = 6
students[john][grades] = {math:3.3}

students[jane] = {}
students[jane][age] = 12
students[jane][gender] = "female"
students[jane][level] = 6
students[jane][grades] = {math:3.5}

# At this point, we need to remember who the students are and where the grades are stored. Not a huge deal, but avoided by OOP.
print(calculateGPA(students[john][grades]))
print(calculateGPA(students[jane][grades]))
89
dantiston

Chaque fois que vous avez besoin de maintenir un état de vos fonctions et que cela ne peut pas être accompli avec des générateurs (fonctions qui cèdent plutôt que de revenir). Les générateurs conservent leur propre état.

Si vous souhaitez remplacer l'un des opérateurs standard, vous avez besoin d'une classe.

Chaque fois que vous utilisez un modèle de visiteur, vous aurez besoin de classes. Tous les autres modèles de conception peuvent être réalisés plus efficacement et plus proprement avec des générateurs, des gestionnaires de contexte (qui sont également mieux implémentés en tant que générateurs qu'en tant que classes) et des types POD (dictionnaires, listes et tuples, etc.).

Si vous voulez écrire du code "Pythonic", vous devriez préférer les gestionnaires de contexte et les générateurs aux classes. Ce sera plus propre.

Si vous souhaitez étendre les fonctionnalités, vous serez presque toujours en mesure de l’accomplir avec un confinement plutôt qu’un héritage.

Comme toute règle, ceci a une exception. Si vous souhaitez encapsuler rapidement une fonctionnalité (c'est-à-dire écrire un code de test plutôt qu'un code réutilisable au niveau de la bibliothèque), vous pouvez encapsuler l'état dans une classe. Ce sera simple et n'aura pas besoin d'être réutilisable.

Si vous avez besoin d'un destructeur de style C++ (RIIA), vous ne voulez certainement PAS utiliser de classes. Vous voulez des gestionnaires de contexte.

14
Dmitry Rubanovich

Je pense que tu le fais bien. Les cours sont raisonnables lorsque vous devez simuler une logique métier ou des processus difficiles de la vie réelle avec des relations difficiles. À titre d'exemple:

  • Plusieurs fonctions avec l'état de partage
  • Plus d'une copie des mêmes variables d'état
  • Pour étendre le comportement d'une fonctionnalité existante

Je vous suggère également de regarder cette vidéo classique

10
valignatev

Une classe définit une entité du monde réel. Si vous travaillez sur quelque chose qui existe individuellement et a sa propre logique qui est séparée des autres, vous devriez créer une classe pour cela. Par exemple, une classe qui encapsule la connectivité de la base de données.

Si ce n'est pas le cas, pas besoin de créer de classe

4
Ashutosh

Cela dépend de votre idée et de votre conception. si vous êtes un bon concepteur, les POO apparaîtront naturellement sous la forme de divers modèles. Pour un traitement simple au niveau script, les POO peuvent être surchargés. Simple considérez les avantages de base des POO comme réutilisables et extensibles et assurez-vous qu’ils sont nécessaires ou non. Les POO rendent les choses complexes plus simples et les choses simples. Simplement garder les choses simples dans les deux sens en utilisant des POO ou non en utilisant des POO. qui est toujours plus simple que cela.

0
Mohit Thakur