Conventions de passage d'arguments de fonction

PythonPythonBeginner
Pratiquer maintenant

This tutorial is from open-source community. Access the source code

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Dans ce laboratoire, vous allez apprendre les conventions de passage d'arguments des fonctions Python. Vous allez également créer une structure réutilisable pour les classes de données et appliquer les principes de conception orientée objet pour simplifier votre code.

Cet exercice vise à réécrire le fichier stock.py d'une manière plus organisée. Avant de commencer, copiez votre travail existant dans le fichier stock.py dans un nouveau fichier nommé orig_stock.py pour référence. Les fichiers que vous allez créer sont structure.py et stock.py.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python/FunctionsGroup -.-> python/arguments_return("Arguments and Return Values") python/FunctionsGroup -.-> python/keyword_arguments("Keyword Arguments") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/ObjectOrientedProgrammingGroup -.-> python/constructor("Constructor") python/ObjectOrientedProgrammingGroup -.-> python/inheritance("Inheritance") python/ObjectOrientedProgrammingGroup -.-> python/encapsulation("Encapsulation") subgraph Lab Skills python/arguments_return -.-> lab-132509{{"Conventions de passage d'arguments de fonction"}} python/keyword_arguments -.-> lab-132509{{"Conventions de passage d'arguments de fonction"}} python/classes_objects -.-> lab-132509{{"Conventions de passage d'arguments de fonction"}} python/constructor -.-> lab-132509{{"Conventions de passage d'arguments de fonction"}} python/inheritance -.-> lab-132509{{"Conventions de passage d'arguments de fonction"}} python/encapsulation -.-> lab-132509{{"Conventions de passage d'arguments de fonction"}} end

Comprendre le passage d'arguments de fonction

En Python, les fonctions sont un concept fondamental qui vous permet de regrouper un ensemble d'instructions pour effectuer une tâche spécifique. Lorsque vous appelez une fonction, vous devez souvent lui fournir des données, que nous appelons des arguments. Python propose différentes manières de passer ces arguments aux fonctions. Cette flexibilité est incroyablement utile car elle vous aide à écrire un code plus propre et plus maintenable. Avant d'appliquer ces techniques à notre projet, examinons de plus près ces conventions de passage d'arguments.

Créer une sauvegarde de votre travail

Avant de commencer à apporter des modifications à notre fichier stock.py, il est recommandé de créer une sauvegarde. Ainsi, si quelque chose se passe mal lors de nos expériences, nous pouvons toujours revenir à la version originale. Pour créer une sauvegarde, ouvrez un terminal et exécutez la commande suivante :

cp stock.py orig_stock.py

Cette commande utilise la commande cp (copier) dans le terminal. Elle prend le fichier stock.py et en crée une copie nommée orig_stock.py. En faisant cela, nous nous assurons que notre travail original est sauvegardé en toute sécurité.

Explorer le passage d'arguments de fonction

En Python, il existe plusieurs façons d'appeler des fonctions avec différents types d'arguments. Explorons en détail chaque méthode.

1. Arguments positionnels

La manière la plus simple de passer des arguments à une fonction est par position. Lorsque vous définissez une fonction, vous spécifiez une liste de paramètres. Lorsque vous appelez la fonction, vous fournissez des valeurs pour ces paramètres dans le même ordre que celui de leur définition.

Voici un exemple :

def calculate(x, y, z):
    return x + y + z

## Appel avec des arguments positionnels
result = calculate(1, 2, 3)
print(result)  ## Sortie : 6

Dans cet exemple, la fonction calculate prend trois paramètres : x, y et z. Lorsque nous appelons la fonction avec calculate(1, 2, 3), la valeur 1 est assignée à x, 2 est assignée à y et 3 est assignée à z. La fonction additionne ensuite ces valeurs et renvoie le résultat.

2. Arguments nommés (Keyword Arguments)

En plus des arguments positionnels, vous pouvez également spécifier des arguments par leur nom. Cela s'appelle l'utilisation d'arguments nommés. Lorsque vous utilisez des arguments nommés, vous n'avez pas à vous soucier de l'ordre des arguments.

Voici un exemple :

## Appel avec un mélange d'arguments positionnels et nommés
result = calculate(1, z=3, y=2)
print(result)  ## Sortie : 6

Dans cet exemple, nous passons d'abord l'argument positionnel 1 pour x. Ensuite, nous utilisons des arguments nommés pour spécifier les valeurs de y et z. L'ordre des arguments nommés n'a pas d'importance, tant que vous fournissez les noms corrects.

3. Désempilement de séquences et de dictionnaires

Python propose un moyen pratique de passer des séquences et des dictionnaires en tant qu'arguments en utilisant la syntaxe * et **. Cela s'appelle le désempilement (unpacking).

Voici un exemple de désempilement d'un tuple en arguments positionnels :

## Désempilement d'un tuple en arguments positionnels
args = (1, 2, 3)
result = calculate(*args)
print(result)  ## Sortie : 6

Dans cet exemple, nous avons un tuple args qui contient les valeurs 1, 2 et 3. Lorsque nous utilisons l'opérateur * avant args dans l'appel de fonction, Python désempile le tuple et passe ses éléments en tant qu'arguments positionnels à la fonction calculate.

Voici un exemple de désempilement d'un dictionnaire en arguments nommés :

## Désempilement d'un dictionnaire en arguments nommés
kwargs = {'y': 2, 'z': 3}
result = calculate(1, **kwargs)
print(result)  ## Sortie : 6

Dans cet exemple, nous avons un dictionnaire kwargs qui contient les paires clé-valeur 'y': 2 et 'z': 3. Lorsque nous utilisons l'opérateur ** avant kwargs dans l'appel de fonction, Python désempile le dictionnaire et passe ses paires clé-valeur en tant qu'arguments nommés à la fonction calculate.

4. Accepter un nombre variable d'arguments

Parfois, vous pouvez vouloir définir une fonction qui peut accepter un nombre quelconque d'arguments. Python vous permet de le faire en utilisant la syntaxe * et ** dans la définition de la fonction.

Voici un exemple d'une fonction qui accepte un nombre quelconque d'arguments positionnels :

## Accepter un nombre quelconque d'arguments positionnels
def sum_all(*args):
    return sum(args)

print(sum_all(1, 2))           ## Sortie : 3
print(sum_all(1, 2, 3, 4, 5))  ## Sortie : 15

Dans cet exemple, la fonction sum_all utilise le paramètre *args pour accepter un nombre quelconque d'arguments positionnels. L'opérateur * collecte tous les arguments positionnels dans un tuple nommé args. La fonction utilise ensuite la fonction intégrée sum pour additionner tous les éléments du tuple.

Voici un exemple d'une fonction qui accepte un nombre quelconque d'arguments nommés :

## Accepter un nombre quelconque d'arguments nommés
def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Python", year=1991)
## Sortie :
## name: Python
## year: 1991

Dans cet exemple, la fonction print_info utilise le paramètre **kwargs pour accepter un nombre quelconque d'arguments nommés. L'opérateur ** collecte tous les arguments nommés dans un dictionnaire nommé kwargs. La fonction itère ensuite sur les paires clé-valeur du dictionnaire et les affiche.

Ces techniques nous aideront à créer des structures de code plus flexibles et réutilisables dans les étapes suivantes. Pour vous familiariser avec ces concepts, ouvrons l'interpréteur Python et essayons quelques-uns de ces exemples.

python3

Une fois que vous êtes dans l'interpréteur Python, essayez d'entrer les exemples ci-dessus. Cela vous donnera une expérience pratique des techniques de passage d'arguments.

Création d'une classe de base pour les structures

Maintenant que nous comprenons bien le passage d'arguments de fonction, nous allons créer une classe de base réutilisable pour les structures de données. Cette étape est cruciale car elle nous aide à éviter d'écrire le même code à plusieurs reprises lorsque nous créons de simples classes pour stocker des données. En utilisant une classe de base, nous pouvons rationaliser notre code et le rendre plus efficace.

Le problème du code répétitif

Dans les exercices précédents, vous avez défini une classe Stock comme indiqué ci - dessous :

class Stock:
    def __init__(self, name, shares, price):
        self.name = name
        self.shares = shares
        self.price = price

Regardez attentivement la méthode __init__. Vous remarquerez qu'elle est assez répétitive. Vous devez attribuer manuellement chaque attribut un par un. Cela peut devenir très fastidieux et chronophage, surtout lorsque vous avez de nombreuses classes avec un grand nombre d'attributs.

Création d'une classe de base flexible

Créons une classe de base Structure qui peut gérer automatiquement l'attribution des attributs. Tout d'abord, ouvrez le WebIDE et créez un nouveau fichier nommé structure.py. Ensuite, ajoutez le code suivant à ce fichier :

## structure.py

class Structure:
    """
    A base class for creating simple data structures.
    Automatically populates object attributes from _fields and constructor arguments.
    """
    _fields = ()

    def __init__(self, *args):
        ## Check that the number of arguments matches the number of fields
        if len(args) != len(self._fields):
            raise TypeError(f"Expected {len(self._fields)} arguments")

        ## Set the attributes
        for name, value in zip(self._fields, args):
            setattr(self, name, value)

Cette classe de base a plusieurs caractéristiques importantes :

  1. Elle définit une variable de classe _fields. Par défaut, cette variable est vide. Cette variable contiendra les noms des attributs que la classe aura.
  2. Elle vérifie si le nombre d'arguments passés au constructeur correspond au nombre de champs définis dans _fields. Si ce n'est pas le cas, elle lève une erreur TypeError. Cela nous aide à détecter les erreurs tôt.
  3. Elle définit les attributs de l'objet en utilisant les noms de champ et les valeurs fournies en tant qu'arguments. La fonction setattr est utilisée pour définir dynamiquement les attributs.

Test de notre classe de base Structure

Maintenant, créons quelques classes d'exemple qui héritent de la classe de base Structure. Ajoutez le code suivant à votre fichier structure.py :

## Example classes using Structure
class Stock(Structure):
    _fields = ('name', 'shares', 'price')

class Point(Structure):
    _fields = ('x', 'y')

class Date(Structure):
    _fields = ('year', 'month', 'day')

Pour tester si notre implémentation fonctionne correctement, nous allons créer un fichier de test nommé test_structure.py. Ajoutez le code suivant à ce fichier :

## test_structure.py
from structure import Stock, Point, Date

## Test Stock class
s = Stock('GOOG', 100, 490.1)
print(f"Stock name: {s.name}, shares: {s.shares}, price: {s.price}")

## Test Point class
p = Point(3, 4)
print(f"Point coordinates: ({p.x}, {p.y})")

## Test Date class
d = Date(2023, 11, 9)
print(f"Date: {d.year}-{d.month}-{d.day}")

## Test error handling
try:
    s2 = Stock('AAPL', 50)  ## Missing price argument
    print("This should not print")
except TypeError as e:
    print(f"Error correctly caught: {e}")

Pour exécuter le test, ouvrez votre terminal et exécutez la commande suivante :

python3 test_structure.py

Vous devriez voir la sortie suivante :

Stock name: GOOG, shares: 100, price: 490.1
Point coordinates: (3, 4)
Date: 2023-11-9
Error correctly caught: Expected 3 arguments

Comme vous pouvez le voir, notre classe de base fonctionne comme prévu. Elle a rendu beaucoup plus facile la définition de nouvelles structures de données sans avoir à écrire le même code de base à plusieurs reprises.

✨ Vérifier la solution et pratiquer

Amélioration de la représentation des objets

Notre classe Structure est utile pour créer et accéder aux objets. Cependant, elle n'a actuellement pas un bon moyen de se représenter sous forme de chaîne de caractères. Lorsque vous affichez un objet ou le visualisez dans l'interpréteur Python, vous souhaitez voir un affichage clair et informatif. Cela vous aide à comprendre de quoi l'objet est constitué et quelles sont ses valeurs.

Comprendre la représentation des objets en Python

En Python, il existe deux méthodes spéciales utilisées pour représenter les objets de différentes manières. Ces méthodes sont importantes car elles vous permettent de contrôler la façon dont vos objets sont affichés.

  • __str__ - Cette méthode est utilisée par la fonction str() et la fonction print(). Elle fournit une représentation lisible par l'homme de l'objet. Par exemple, si vous avez un objet Stock, la méthode __str__ pourrait retourner quelque chose comme "Stock: GOOG, 100 shares at $490.1".
  • __repr__ - Cette méthode est utilisée par l'interpréteur Python et la fonction repr(). Elle donne une représentation plus technique et non ambiguë de l'objet. L'objectif de __repr__ est de fournir une chaîne de caractères qui peut être utilisée pour recréer l'objet. Par exemple, pour un objet Stock, elle pourrait retourner "Stock('GOOG', 100, 490.1)".

Ajoutons une méthode __repr__ à notre classe Structure. Cela facilitera le débogage de notre code car nous pourrons clairement voir l'état de nos objets.

Implémentation d'une bonne représentation

Maintenant, vous devez mettre à jour votre fichier structure.py. Vous allez ajouter la méthode __repr__ à la classe Structure. Cette méthode créera une chaîne de caractères représentant l'objet d'une manière qui peut être utilisée pour le recréer.

def __repr__(self):
    """
    Return a representation of the object that can be used to recreate it.
    Example: Stock('GOOG', 100, 490.1)
    """
    ## Get the class name
    cls_name = type(self).__name__

    ## Get all the field values
    values = [getattr(self, name) for name in self._fields]

    ## Format the fields and values
    args_str = ', '.join(repr(value) for value in values)

    ## Return the formatted string
    return f"{cls_name}({args_str})"

Voici ce que cette méthode fait étape par étape :

  1. Elle obtient le nom de la classe en utilisant type(self).__name__. Cela est important car il vous indique de quel type d'objet vous avez affaire.
  2. Elle récupère toutes les valeurs des champs de l'instance. Cela vous donne les données que l'objet contient.
  3. Elle crée une représentation sous forme de chaîne de caractères avec le nom de la classe et les valeurs. Cette chaîne de caractères peut être utilisée pour recréer l'objet.

Test de la représentation améliorée

Testons notre implémentation améliorée. Créez un nouveau fichier appelé test_repr.py. Ce fichier créera quelques instances de nos classes et affichera leurs représentations.

## test_repr.py
from structure import Stock, Point, Date

## Create instances
s = Stock('GOOG', 100, 490.1)
p = Point(3, 4)
d = Date(2023, 11, 9)

## Print the representations
print(repr(s))
print(repr(p))
print(repr(d))

## Direct printing also uses __repr__ in the interpreter
print(s)
print(p)
print(d)

Pour exécuter le test, ouvrez votre terminal et entrez la commande suivante :

python3 test_repr.py

Vous devriez voir la sortie suivante :

Stock('GOOG', 100, 490.1)
Point(3, 4)
Date(2023, 11, 9)
Stock('GOOG', 100, 490.1)
Point(3, 4)
Date(2023, 11, 9)

Cette sortie est beaucoup plus informative que précédemment. Lorsque vous voyez Stock('GOOG', 100, 490.1), vous savez immédiatement de quoi l'objet est constitué. Vous pourriez même copier cette chaîne de caractères et l'utiliser pour recréer l'objet dans votre code.

L'avantage d'une bonne représentation

Une bonne implémentation de __repr__ est très utile pour le débogage. Lorsque vous examinez des objets dans l'interpréteur ou les enregistrez pendant l'exécution du programme, une représentation claire facilite l'identification rapide des problèmes. Vous pouvez voir l'état exact de l'objet et comprendre ce qui peut ne pas fonctionner.

✨ Vérifier la solution et pratiquer

Restriction des noms d'attributs

Actuellement, notre classe Structure permet de définir n'importe quel attribut sur ses instances. Pour les débutants, cela peut sembler pratique au départ, mais cela peut en réalité entraîner de nombreux problèmes. Lorsque vous travaillez avec une classe, vous vous attendez à ce que certains attributs soient présents et utilisés d'une manière spécifique. Si les utilisateurs font une faute de frappe dans les noms d'attributs ou essaient de définir des attributs qui ne font pas partie de la conception originale, cela peut entraîner des erreurs difficiles à trouver.

La nécessité de restreindre les attributs

Examinons un scénario simple pour comprendre pourquoi nous devons restreindre les noms d'attributs. Considérez le code suivant :

s = Stock('GOOG', 100, 490.1)
s.shares = 50      ## Correct attribute name
s.share = 60       ## Typo in attribute name - creates a new attribute instead of updating

Dans la deuxième ligne, il y a une faute de frappe. Au lieu de shares, nous avons écrit share. En Python, au lieu de lever une erreur, il créera simplement un nouvel attribut appelé share. Cela peut entraîner des bugs subtils car vous pourriez penser que vous mettez à jour l'attribut shares, mais vous créez en réalité un nouvel attribut. Cela peut faire que votre code se comporte de manière inattendue et soit très difficile à déboguer.

Implémentation de la restriction des attributs

Pour résoudre ce problème, nous pouvons redéfinir la méthode __setattr__. Cette méthode est appelée chaque fois que vous essayez de définir un attribut sur un objet. En la redéfinissant, nous pouvons contrôler quels attributs peuvent être définis et lesquels ne peuvent pas.

Mettez à jour votre classe Structure dans le fichier structure.py avec le code suivant :

def __setattr__(self, name, value):
    """
    Restrict attribute setting to only those defined in _fields
    or attributes starting with underscore (private attributes).
    """
    if name.startswith('_'):
        ## Allow setting private attributes (starting with '_')
        super().__setattr__(name, value)
    elif name in self._fields:
        ## Allow setting attributes defined in _fields
        super().__setattr__(name, value)
    else:
        ## Raise an error for other attributes
        raise AttributeError(f'No attribute {name}')

Voici comment cette méthode fonctionne :

  1. Si le nom de l'attribut commence par un tiret bas (_), il est considéré comme un attribut privé. Les attributs privés sont souvent utilisés à des fins internes dans une classe. Nous autorisons la définition de ces attributs car ils font partie de l'implémentation interne de la classe.
  2. Si le nom de l'attribut est dans la liste _fields, cela signifie qu'il s'agit d'un des attributs définis dans la conception de la classe. Nous autorisons la définition de ces attributs car ils font partie du comportement attendu de la classe.
  3. Si le nom de l'attribut ne répond à aucune de ces conditions, nous levons une erreur AttributeError. Cela indique à l'utilisateur qu'il essaie de définir un attribut qui n'existe pas dans la classe.

Test de la restriction des attributs

Maintenant que nous avons implémenté la restriction des attributs, testons-la pour nous assurer qu'elle fonctionne comme prévu. Créez un fichier nommé test_attributes.py avec le code suivant :

## test_attributes.py
from structure import Stock

s = Stock('GOOG', 100, 490.1)

## This should work - valid attribute
print("Setting shares to 50")
s.shares = 50
print(f"Shares is now: {s.shares}")

## This should work - private attribute
print("\nSetting _internal_data")
s._internal_data = "Some data"
print(f"_internal_data is: {s._internal_data}")

## This should fail - invalid attribute
print("\nTrying to set an invalid attribute:")
try:
    s.share = 60  ## Typo in attribute name
    print("This should not print")
except AttributeError as e:
    print(f"Error correctly caught: {e}")

Pour exécuter le test, ouvrez votre terminal et entrez la commande suivante :

python3 test_attributes.py

Vous devriez voir la sortie suivante :

Setting shares to 50
Shares is now: 50

Setting _internal_data
_internal_data is: Some data

Trying to set an invalid attribute:
Error correctly caught: No attribute share

Cette sortie montre que notre classe empêche désormais les erreurs accidentelles d'attributs. Elle nous permet de définir des attributs valides et des attributs privés, mais elle lève une erreur lorsque nous essayons de définir un attribut invalide.

L'importance de la restriction des attributs

Restreindre les noms d'attributs est très important pour écrire un code robuste et maintenable. Voici pourquoi :

  1. Cela permet de détecter les fautes de frappe dans les noms d'attributs. Si vous faites une erreur lors de la saisie d'un nom d'attribut, le code lèvera une erreur au lieu de créer un nouvel attribut. Cela facilite la détection et la correction des erreurs dès le début du processus de développement.
  2. Cela empêche les tentatives de définir des attributs qui n'existent pas dans la conception de la classe. Cela garantit que la classe est utilisée comme prévu et que le code se comporte de manière prévisible.
  3. Cela évite la création accidentelle de nouveaux attributs. La création de nouveaux attributs peut entraîner un comportement inattendu et rendre le code plus difficile à comprendre et à maintenir.

En restreignant les noms d'attributs, nous rendons notre code plus fiable et plus facile à utiliser.

✨ Vérifier la solution et pratiquer

Refonte de la classe Stock

Maintenant que nous avons une classe de base Structure bien définie, il est temps de refaire notre classe Stock. En utilisant cette classe de base, nous pouvons simplifier notre code et le rendre plus organisé. La classe Structure fournit un ensemble de fonctionnalités communes que nous pouvons réutiliser dans notre classe Stock, ce qui est un grand atout pour la maintenance et la lisibilité du code.

Création de la nouvelle classe Stock

Commençons par créer un nouveau fichier nommé stock.py. Ce fichier contiendra notre classe Stock refaite. Voici le code que vous devez placer dans le fichier stock.py :

## stock.py
from structure import Structure

class Stock(Structure):
    _fields = ('name', 'shares', 'price')

    @property
    def cost(self):
        """
        Calculate the cost as shares * price
        """
        return self.shares * self.price

    def sell(self, nshares):
        """
        Sell a number of shares
        """
        self.shares -= nshares

Analysons ce que fait cette nouvelle classe Stock :

  1. Elle hérite de la classe Structure. Cela signifie que la classe Stock peut utiliser toutes les fonctionnalités fournies par la classe Structure. L'un des avantages est que nous n'avons pas besoin d'écrire nous - mêmes une méthode __init__ car la classe Structure s'occupe automatiquement de l'affectation des attributs.
  2. Nous définissons _fields, qui est un tuple spécifiant les attributs de la classe Stock. Ces attributs sont name, shares et price.
  3. La propriété cost est définie pour calculer le coût total du stock. Elle multiplie le nombre de shares par le price.
  4. La méthode sell est utilisée pour réduire le nombre de parts. Lorsque vous appelez cette méthode avec un nombre de parts à vendre, elle soustrait ce nombre du nombre actuel de parts.

Test de la nouvelle classe Stock

Pour nous assurer que notre nouvelle classe Stock fonctionne comme prévu, nous devons créer un fichier de test. Créons un fichier nommé test_stock.py avec le code suivant :

## test_stock.py
from stock import Stock

## Create a stock
s = Stock('GOOG', 100, 490.1)

## Check the attributes
print(f"Stock: {s}")
print(f"Name: {s.name}")
print(f"Shares: {s.shares}")
print(f"Price: {s.price}")
print(f"Cost: {s.cost}")

## Sell some shares
print("\nSelling 20 shares...")
s.sell(20)
print(f"Shares after selling: {s.shares}")
print(f"Cost after selling: {s.cost}")

## Try to set an invalid attribute
print("\nTrying to set an invalid attribute:")
try:
    s.prices = 500  ## Invalid attribute (should be 'price')
    print("This should not print")
except AttributeError as e:
    print(f"Error correctly caught: {e}")

Dans ce fichier de test, nous importons d'abord la classe Stock depuis le fichier stock.py. Ensuite, nous créons une instance de la classe Stock avec le nom 'GOOG', 100 parts et un prix de 490.1. Nous affichons les attributs du stock pour vérifier s'ils sont correctement définis. Après cela, nous vendons 20 parts et affichons le nouveau nombre de parts et le nouveau coût. Enfin, nous essayons de définir un attribut invalide prices (il devrait s'agir de price). Si notre classe Stock fonctionne correctement, elle devrait lever une erreur AttributeError.

Pour exécuter le test, ouvrez votre terminal et entrez la commande suivante :

python3 test_stock.py

La sortie attendue est la suivante :

Stock: Stock('GOOG', 100, 490.1)
Name: GOOG
Shares: 100
Price: 490.1
Cost: 49010.0

Selling 20 shares...
Shares after selling: 80
Cost after selling: 39208.0

Trying to set an invalid attribute:
Error correctly caught: No attribute prices

Exécution de tests unitaires

Si vous avez des tests unitaires provenant d'exercices précédents, vous pouvez les exécuter sur votre nouvelle implémentation. Dans votre terminal, entrez la commande suivante :

python3 teststock.py

Notez que certains tests peuvent échouer. Cela peut être parce qu'ils attendent des comportements ou des méthodes spécifiques que nous n'avons pas encore implémentées. Ne vous inquiétez pas ! Nous continuerons à construire sur cette base dans les exercices suivants.

Revue de nos progrès

Prenons un moment pour revoir ce que nous avons accompli jusqu'à présent :

  1. Nous avons créé une classe de base Structure réutilisable. Cette classe :

    • Gère automatiquement l'affectation des attributs, ce qui nous évite d'écrire beaucoup de code répétitif.
    • Fournit une bonne représentation sous forme de chaîne de caractères, facilitant l'affichage et le débogage de nos objets.
    • Restreint les noms d'attributs pour éviter les erreurs, ce qui rend notre code plus robuste.
  2. Nous avons refait notre classe Stock. Elle :

    • Hérite de la classe Structure pour réutiliser la fonctionnalité commune.
    • Ne définit que les champs et les méthodes spécifiques au domaine, ce qui garde la classe ciblée et propre.
    • A une conception claire et simple, facilitant sa compréhension et sa maintenance.

Cette approche présente plusieurs avantages pour notre code :

  • Il est plus facilement maintenable car il y a moins de répétition. Si nous devons changer quelque chose dans la fonctionnalité commune, nous n'avons besoin de le changer que dans la classe Structure.
  • Il est plus robuste grâce aux meilleures vérifications d'erreurs fournies par la classe Structure.
  • Il est plus lisible car les responsabilités de chaque classe sont claires.

Dans les exercices suivants, nous continuerons à construire sur cette base pour créer un système de gestion de portefeuille d'actions plus sophistiqué.

✨ Vérifier la solution et pratiquer

Résumé

Dans ce laboratoire (lab), vous avez appris les conventions de passage d'arguments de fonction en Python et les avez appliquées pour construire une base de code plus organisée et maintenable. Vous avez exploré les mécanismes flexibles de passage d'arguments de Python, créé une classe de base Structure réutilisable pour les objets de données et amélioré la représentation des objets pour faciliter le débogage.

Vous avez également ajouté une validation d'attributs pour éviter les erreurs courantes et refait la classe Stock en utilisant la nouvelle structure. Ces techniques illustrent les principes clés de la conception orientée objet tels que l'héritage pour la réutilisation du code, l'encapsulation pour l'intégrité des données et le polymorphisme grâce à des interfaces communes. En appliquant ces principes, vous pouvez développer un code plus robuste et maintenable avec moins de répétition et moins d'erreurs.