Comment gérer les arguments de fonction manquants ou invalides en Python

PythonPythonBeginner
Pratiquer maintenant

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

Introduction

Les arguments de fonction en Python sont un aspect fondamental de la programmation, mais gérer les arguments manquants ou invalides peut être un défi. Ce tutoriel vous guidera à travers la gestion des arguments de fonction en Python, de la compréhension des bases à la mise en œuvre de stratégies robustes de validation et de gestion des erreurs. À la fin, vous serez capable d'écrire du code Python qui gère avec élégance les arguments de fonction, ce qui conduit à des applications plus fiables et maintenables.

Comprendre les arguments de fonction de base et les valeurs par défaut

En Python, les fonctions sont des blocs de code réutilisables qui effectuent des tâches spécifiques. Lorsque vous définissez une fonction, vous pouvez spécifier des paramètres que la fonction doit recevoir. Apprenons à configurer des fonctions avec différents types d'arguments et à explorer comment fournir des valeurs par défaut.

Création de notre première fonction

Commençons par créer un simple fichier Python avec lequel travailler. Dans le WebIDE, accédez au répertoire du projet et créez un nouveau fichier appelé function_args.py :

  1. Cliquez sur le menu "File" dans le WebIDE
  2. Sélectionnez "New File"
  3. Entrez function_args.py comme nom de fichier
  4. Cliquez sur "OK"

Maintenant, ajoutons une fonction de base à ce fichier :

def greet(name):
    """A simple function that greets a person by name."""
    return f"Hello, {name}!"

## Call the function and print the result
result = greet("Alice")
print(result)

Enregistrez le fichier (Ctrl+S ou File > Save) puis exécutez-le dans le terminal :

python3 function_args.py

Vous devriez voir la sortie :

Hello, Alice!

Comprendre les arguments requis

Dans l'exemple ci-dessus, name est un argument requis. Si vous essayez d'appeler la fonction sans fournir cet argument, Python lèvera une erreur.

Modifions notre fichier pour le démontrer :

def greet(name):
    """A simple function that greets a person by name."""
    return f"Hello, {name}!"

## This will work
result = greet("Alice")
print(result)

## This will raise an error
try:
    result = greet()
    print(result)
except TypeError as e:
    print(f"Error: {e}")

Enregistrez le fichier et exécutez-le :

python3 function_args.py

Sortie :

Hello, Alice!
Error: greet() missing 1 required positional argument: 'name'

Comme vous pouvez le constater, Python lève une TypeError lorsque nous ne fournissons pas l'argument requis.

Ajout de valeurs par défaut

Pour rendre les arguments optionnels, nous pouvons fournir des valeurs par défaut. Mettons à jour notre fonction :

def greet(name="Guest"):
    """A function that greets a person by name, with a default value."""
    return f"Hello, {name}!"

## With an argument
result1 = greet("Alice")
print(result1)

## Without an argument - uses the default value
result2 = greet()
print(result2)

Enregistrez et exécutez :

python3 function_args.py

Sortie :

Hello, Alice!
Hello, Guest!

Maintenant, la fonction fonctionne avec et sans argument.

Arguments multiples avec valeurs par défaut

Développons notre fonction pour gérer plusieurs arguments, certains avec des valeurs par défaut :

def greet(name="Guest", message="Hello", punctuation="!"):
    """A function with multiple arguments and default values."""
    return f"{message}, {name}{punctuation}"

## Using all default values
print(greet())

## Providing only the name
print(greet("Alice"))

## Providing name and message
print(greet("Bob", "Hi"))

## Providing all arguments
print(greet("Charlie", "Welcome", "!!!"))

Enregistrez et exécutez :

python3 function_args.py

Sortie :

Hello, Guest!
Hello, Alice!
Hi, Bob!
Welcome, Charlie!!!

Utilisation des arguments nommés (Keyword Arguments)

Vous pouvez également spécifier des arguments par leur nom, quel que soit leur ordre :

def greet(name="Guest", message="Hello", punctuation="!"):
    """A function with multiple arguments and default values."""
    return f"{message}, {name}{punctuation}"

## Using keyword arguments
print(greet(message="Hey", name="David"))
print(greet(punctuation="...", message="Welcome back", name="Emma"))

Enregistrez et exécutez :

python3 function_args.py

Sortie :

Hey, David!
Welcome back, Emma...

Ceci est particulièrement utile lorsqu'une fonction a de nombreux arguments et que vous ne souhaitez en spécifier que quelques-uns.

Vous savez maintenant comment créer des fonctions avec des arguments par défaut et comment utiliser les arguments nommés. Dans la prochaine étape, nous explorerons des méthodes plus avancées pour gérer les arguments de fonction manquants ou invalides.

Gérer les arguments manquants avec *args et **kwargs

En Python, nous devons parfois créer des fonctions flexibles qui peuvent accepter un nombre variable d'arguments. Pour gérer ces cas, Python fournit deux éléments de syntaxe spéciaux : *args et **kwargs.

Création d'un nouveau fichier Python

Créons un nouveau fichier pour travailler avec ces concepts :

  1. Cliquez sur le menu "File" dans le WebIDE
  2. Sélectionnez "New File"
  3. Entrez flexible_args.py comme nom de fichier
  4. Cliquez sur "OK"

Comprendre *args

La syntaxe *args permet à une fonction d'accepter un nombre quelconque d'arguments positionnels, qui sont collectés dans un tuple.

Ajoutez le code suivant à flexible_args.py :

def sum_numbers(*args):
    """A function that sums up any number of arguments."""
    result = 0
    for num in args:
        result += num
    return result

## Test the function with different numbers of arguments
print(f"Sum of 1, 2: {sum_numbers(1, 2)}")
print(f"Sum of 1, 2, 3, 4, 5: {sum_numbers(1, 2, 3, 4, 5)}")
print(f"No arguments: {sum_numbers()}")

Enregistrez le fichier et exécutez-le :

python3 flexible_args.py

Sortie :

Sum of 1, 2: 3
Sum of 1, 2, 3, 4, 5: 15
No arguments: 0

Cela démontre comment *args peut gérer n'importe quel nombre d'arguments, y compris aucun. À l'intérieur de la fonction, args est un tuple contenant tous les arguments fournis.

Comprendre **kwargs

La syntaxe **kwargs permet à une fonction d'accepter un nombre quelconque d'arguments nommés (keyword arguments), qui sont collectés dans un dictionnaire.

Ajoutons une autre fonction à notre fichier :

def build_profile(**kwargs):
    """A function that builds a user profile from keyword arguments."""
    profile = {}

    ## Add required fields with defaults
    profile["name"] = kwargs.get("name", "Anonymous")
    profile["age"] = kwargs.get("age", "Not specified")

    ## Add any additional fields
    for key, value in kwargs.items():
        if key not in ["name", "age"]:
            profile[key] = value

    return profile

## Test the function with different keyword arguments
print("Basic profile:", build_profile())
print("Full profile:", build_profile(name="Alice", age=30, occupation="Developer", location="New York"))
print("Custom fields:", build_profile(hobby="Reading", favorite_color="Blue"))

Enregistrez et exécutez :

python3 flexible_args.py

Sortie :

Basic profile: {'name': 'Anonymous', 'age': 'Not specified'}
Full profile: {'name': 'Alice', 'age': 30, 'occupation': 'Developer', 'location': 'New York'}
Custom fields: {'name': 'Anonymous', 'age': 'Not specified', 'hobby': 'Reading', 'favorite_color': 'Blue'}

Remarquez comment kwargs.get("key", default_value) nous permet de récupérer des valeurs avec des valeurs par défaut si elles n'existent pas.

Combiner Required, Default, *args et **kwargs

Créons une fonction plus complexe qui combine tous ces types d'arguments :

def format_message(recipient, message="Hello", *args, **kwargs):
    """
    A function that formats a message with various customization options.
    - recipient: Required - who the message is for
    - message: Default greeting
    - *args: Additional message parts
    - **kwargs: Formatting options
    """
    ## Start with the basic message
    full_message = f"{message}, {recipient}!"

    ## Add any additional message parts
    if args:
        full_message += " " + " ".join(args)

    ## Apply formatting options
    if kwargs.get("upper", False):
        full_message = full_message.upper()

    if kwargs.get("wrap", False):
        full_message = f"[{full_message}]"

    return full_message

## Test with different combinations
print(format_message("Alice"))
print(format_message("Bob", "Hi"))
print(format_message("Charlie", "Welcome", "Hope", "you", "are", "well"))
print(format_message("David", "Greetings", upper=True))
print(format_message("Emma", wrap=True))
print(format_message("Frank", "Hey", "How's it going?", upper=True, wrap=True))

Enregistrez et exécutez :

python3 flexible_args.py

Sortie :

Hello, Alice!
Hi, Bob!
Welcome, Charlie! Hope you are well
GREETINGS, DAVID!
[Hello, Emma!]
[HEY, FRANK! HOW'S IT GOING?]

Cet exemple montre comment utiliser tous les types d'arguments de fonction ensemble :

  1. recipient est un argument positionnel requis
  2. message a une valeur par défaut, ce qui le rend optionnel
  3. *args capture tous les arguments positionnels supplémentaires
  4. **kwargs capture tous les arguments nommés (keyword arguments)

En combinant ces approches, vous pouvez créer des fonctions très flexibles qui gèrent les arguments manquants ou optionnels avec élégance.

Validation des arguments de fonction

Lors de la création de fonctions en Python, il est important de vérifier que les arguments passés à vos fonctions sont valides avant de procéder à la logique principale de la fonction. Dans cette étape, nous allons apprendre plusieurs techniques pour valider les arguments de fonction.

Création d'un nouveau fichier Python

Créons un nouveau fichier pour travailler avec les concepts de validation :

  1. Cliquez sur le menu "File" dans le WebIDE
  2. Sélectionnez "New File"
  3. Entrez validate_args.py comme nom de fichier
  4. Cliquez sur "OK"

Validation de base avec des conditionnelles

La façon la plus simple de valider les arguments est d'utiliser des instructions conditionnelles. Commençons par quelques validations de base :

def calculate_rectangle_area(length, width):
    """Calculate the area of a rectangle, validating inputs."""
    ## Validate that inputs are numbers
    if not isinstance(length, (int, float)):
        raise TypeError("Length must be a number")
    if not isinstance(width, (int, float)):
        raise TypeError("Width must be a number")

    ## Validate that inputs are positive
    if length <= 0:
        raise ValueError("Length must be positive")
    if width <= 0:
        raise ValueError("Width must be positive")

    ## Calculate the area
    return length * width

## Test with valid inputs
try:
    area = calculate_rectangle_area(5, 3)
    print(f"Area of rectangle: {area}")
except (TypeError, ValueError) as e:
    print(f"Error: {e}")

## Test with invalid types
try:
    area = calculate_rectangle_area("5", 3)
    print(f"Area of rectangle: {area}")
except (TypeError, ValueError) as e:
    print(f"Error: {e}")

## Test with invalid values
try:
    area = calculate_rectangle_area(5, -3)
    print(f"Area of rectangle: {area}")
except (TypeError, ValueError) as e:
    print(f"Error: {e}")

Enregistrez et exécutez :

python3 validate_args.py

Sortie :

Area of rectangle: 15
Error: Length must be a number
Error: Width must be positive

Cette fonction valide à la fois les types et les valeurs de ses arguments avant d'effectuer des calculs. Lorsque des arguments non valides sont détectés, des messages d'erreur appropriés sont levés.

Utilisation des assertions pour la validation

Une autre façon de valider les arguments est d'utiliser des assertions. Les assertions sont des instructions qui lèvent une AssertionError si une condition n'est pas remplie :

def calculate_discount(price, discount_percent):
    """Calculate the discounted price."""
    ## Assert that inputs are valid
    assert isinstance(price, (int, float)), "Price must be a number"
    assert isinstance(discount_percent, (int, float)), "Discount must be a number"
    assert price >= 0, "Price cannot be negative"
    assert 0 <= discount_percent <= 100, "Discount must be between 0 and 100"

    ## Calculate the discount
    discount_amount = price * (discount_percent / 100)
    return price - discount_amount

## Test with valid inputs
try:
    discounted_price = calculate_discount(100, 20)
    print(f"Discounted price: ${discounted_price}")
except AssertionError as e:
    print(f"Error: {e}")

## Test with invalid discount percentage
try:
    discounted_price = calculate_discount(100, 120)
    print(f"Discounted price: ${discounted_price}")
except AssertionError as e:
    print(f"Error: {e}")

Enregistrez et exécutez :

python3 validate_args.py

Sortie :

Discounted price: $80.0
Error: Discount must be between 0 and 100

Les assertions sont utiles pour le développement et le débogage, mais elles peuvent être désactivées dans le code de production, elles ne sont donc pas toujours le meilleur choix pour la validation dans les applications du monde réel.

Utilisation des annotations de type pour la documentation

Python 3.5+ prend en charge les annotations de type, ce qui peut aider à documenter les types attendus des arguments de fonction et des valeurs de retour. Bien que les annotations de type n'effectuent pas de validation au moment de l'exécution par elles-mêmes, elles fournissent une documentation utile et peuvent être vérifiées par des outils externes comme mypy :

def calculate_average(numbers: list[float]) -> float:
    """Calculate the average of a list of numbers."""
    if not numbers:
        raise ValueError("Cannot calculate average of empty list")

    if not all(isinstance(n, (int, float)) for n in numbers):
        raise TypeError("All elements must be numbers")

    return sum(numbers) / len(numbers)

## Test with valid input
try:
    avg = calculate_average([1, 2, 3, 4, 5])
    print(f"Average: {avg}")
except (TypeError, ValueError) as e:
    print(f"Error: {e}")

## Test with empty list
try:
    avg = calculate_average([])
    print(f"Average: {avg}")
except (TypeError, ValueError) as e:
    print(f"Error: {e}")

## Test with non-numeric elements
try:
    avg = calculate_average([1, 2, "3", 4, 5])
    print(f"Average: {avg}")
except (TypeError, ValueError) as e:
    print(f"Error: {e}")

Enregistrez et exécutez :

python3 validate_args.py

Sortie :

Average: 3.0
Error: Cannot calculate average of empty list
Error: All elements must be numbers

Notez que les annotations de type (list[float] et -> float) n'effectuent aucune validation par elles-mêmes - nous devons toujours écrire notre propre code de validation. Elles servent de documentation et peuvent être vérifiées par des outils externes.

Création d'une fonction robuste avec validation

Maintenant, appliquons toutes ces techniques pour créer une fonction robuste qui calcule le coût total des articles avec une remise :

def calculate_total_cost(items=None, tax_rate=0, discount=0):
    """
    Calculate the total cost of items with tax and discount.

    Args:
        items: List of (item_name, price) tuples
        tax_rate: Tax rate percentage (0-100)
        discount: Discount percentage (0-100)

    Returns:
        A dictionary with the total, subtotal, tax amount, and discount amount
    """
    ## Validate items
    if items is None:
        items = []

    if not isinstance(items, list):
        raise TypeError("Items must be a list")

    ## Validate each item in the list
    for i, item in enumerate(items):
        if not isinstance(item, tuple) or len(item) != 2:
            raise ValueError(f"Item {i} must be a tuple of (name, price)")

        name, price = item
        if not isinstance(name, str):
            raise TypeError(f"Name of item {i} must be a string")
        if not isinstance(price, (int, float)):
            raise TypeError(f"Price of item {i} must be a number")
        if price < 0:
            raise ValueError(f"Price of item {i} cannot be negative")

    ## Validate tax_rate and discount
    if not isinstance(tax_rate, (int, float)):
        raise TypeError("Tax rate must be a number")
    if not isinstance(discount, (int, float)):
        raise TypeError("Discount must be a number")

    if not (0 <= tax_rate <= 100):
        raise ValueError("Tax rate must be between 0 and 100")
    if not (0 <= discount <= 100):
        raise ValueError("Discount must be between 0 and 100")

    ## Calculate the total
    subtotal = sum(price for _, price in items)
    discount_amount = subtotal * (discount / 100)
    tax_amount = (subtotal - discount_amount) * (tax_rate / 100)
    total = subtotal - discount_amount + tax_amount

    return {
        "subtotal": subtotal,
        "discount_amount": discount_amount,
        "tax_amount": tax_amount,
        "total": total
    }

## Test with valid inputs
shopping_cart = [
    ("Laptop", 1000),
    ("Mouse", 25),
    ("Keyboard", 45)
]

try:
    result = calculate_total_cost(shopping_cart, tax_rate=8.5, discount=10)
    print("Shopping Cart Total:")
    for key, value in result.items():
        print(f"  {key.replace('_', ' ').title()}: ${value:.2f}")
except (TypeError, ValueError) as e:
    print(f"Error: {e}")

## Test with invalid item
try:
    invalid_cart = [
        ("Laptop", 1000),
        ("Mouse", "twenty-five"),  ## Invalid price
        ("Keyboard", 45)
    ]
    result = calculate_total_cost(invalid_cart)
    print(result)
except (TypeError, ValueError) as e:
    print(f"Error with invalid item: {e}")

Enregistrez et exécutez :

python3 validate_args.py

Sortie :

Shopping Cart Total:
  Subtotal: $1070.00
  Discount Amount: $107.00
  Tax Amount: $81.86
  Total: $1044.86
Error with invalid item: Price of item 1 must be a number

Cette fonction démontre une validation robuste en :

  1. Vérifiant les types de toutes les entrées
  2. Validant la plage des valeurs numériques
  3. Fournissant des messages d'erreur détaillés
  4. Définissant des valeurs par défaut raisonnables pour les paramètres optionnels
  5. Utilisant des docstrings pour documenter les entrées et les valeurs de retour attendues

En mettant en œuvre une validation approfondie dans vos fonctions, vous pouvez éviter les erreurs, fournir de meilleurs commentaires aux utilisateurs et rendre votre code plus robuste et maintenable.

Building a Complete Application

Now that we have learned various techniques for handling and validating function arguments, let's apply these skills to build a simple but complete application. We will create a basic expense tracking system that demonstrates good practices for function argument handling.

Creating the Application File

Let's create a new Python file for our expense tracker:

  1. Click on the "File" menu in the WebIDE
  2. Select "New File"
  3. Enter expense_tracker.py as the filename
  4. Click "OK"

Designing the Expense Tracker Functions

Our expense tracker will have several functions that handle different aspects of expense management:

def create_expense(description, amount, category=None, date=None):
    """
    Create a new expense entry.

    Args:
        description (str): Description of the expense
        amount (float): The amount spent
        category (str, optional): Category of the expense
        date (str, optional): The date in YYYY-MM-DD format

    Returns:
        dict: An expense entry
    """
    ## Validate description
    if not isinstance(description, str):
        raise TypeError("Description must be a string")
    if not description:
        raise ValueError("Description cannot be empty")

    ## Validate amount
    if not isinstance(amount, (int, float)):
        raise TypeError("Amount must be a number")
    if amount <= 0:
        raise ValueError("Amount must be positive")

    ## Create the expense dictionary
    expense = {
        "description": description,
        "amount": float(amount),
        "category": category or "Uncategorized",
        "date": date or "Not specified"
    }

    return expense


def add_expense_to_list(expenses, **kwargs):
    """
    Add a new expense to the expenses list.

    Args:
        expenses (list): The list of expenses
        **kwargs: The expense details to be passed to create_expense

    Returns:
        list: The updated list of expenses
    """
    ## Validate the expenses list
    if not isinstance(expenses, list):
        raise TypeError("Expenses must be a list")

    ## Extract required arguments
    if "description" not in kwargs:
        raise ValueError("Expense description is required")
    if "amount" not in kwargs:
        raise ValueError("Expense amount is required")

    ## Create the expense and add it to the list
    expense = create_expense(
        kwargs["description"],
        kwargs["amount"],
        kwargs.get("category"),
        kwargs.get("date")
    )

    expenses.append(expense)
    return expenses


def get_total_expenses(expenses, category=None):
    """
    Calculate the total amount of expenses, optionally filtered by category.

    Args:
        expenses (list): The list of expenses
        category (str, optional): Filter by this category if provided

    Returns:
        float: The total amount
    """
    ## Validate the expenses list
    if not isinstance(expenses, list):
        raise TypeError("Expenses must be a list")

    ## Calculate the total
    if category:
        return sum(e["amount"] for e in expenses if e["category"] == category)
    else:
        return sum(e["amount"] for e in expenses)


def get_expense_summary(expenses):
    """
    Get a summary of expenses by category.

    Args:
        expenses (list): The list of expenses

    Returns:
        dict: A dictionary with categories as keys and total amounts as values
    """
    ## Validate the expenses list
    if not isinstance(expenses, list):
        raise TypeError("Expenses must be a list")

    ## Create the summary
    summary = {}
    for expense in expenses:
        category = expense["category"]
        if category in summary:
            summary[category] += expense["amount"]
        else:
            summary[category] = expense["amount"]

    return summary

Using Our Expense Tracker

Now let's use our functions to track some expenses:

def print_expense_summary(summary):
    """Print a formatted summary of expenses by category."""
    print("\nExpense Summary by Category:")
    print("-" * 30)
    for category, amount in summary.items():
        print(f"{category}: ${amount:.2f}")
    print("-" * 30)
    print(f"Total: ${sum(summary.values()):.2f}")

## Initialize an empty expenses list
expenses = []

## Add some expenses
try:
    ## Add with required arguments only
    expenses = add_expense_to_list(
        expenses,
        description="Groceries",
        amount=45.75
    )

    ## Add with all arguments
    expenses = add_expense_to_list(
        expenses,
        description="Movie tickets",
        amount=25.00,
        category="Entertainment",
        date="2023-11-15"
    )

    ## Add another expense
    expenses = add_expense_to_list(
        expenses,
        description="Dinner",
        amount=65.40,
        category="Food",
        date="2023-11-14"
    )

    ## Add with default category
    expenses = add_expense_to_list(
        expenses,
        description="Gas",
        amount=35.80,
        date="2023-11-16"
    )

    ## Display all expenses
    print("All Expenses:")
    for i, expense in enumerate(expenses, 1):
        print(f"{i}. {expense['description']}: ${expense['amount']:.2f} " +
              f"({expense['category']}, {expense['date']})")

    ## Get and display the total
    total = get_total_expenses(expenses)
    print(f"\nTotal expenses: ${total:.2f}")

    ## Get and display expenses for a specific category
    food_total = get_total_expenses(expenses, "Food")
    print(f"Food expenses: ${food_total:.2f}")

    ## Get and display the summary
    summary = get_expense_summary(expenses)
    print_expense_summary(summary)

except (TypeError, ValueError) as e:
    print(f"Error: {e}")

Let's also add some code to demonstrate error handling:

## Try some invalid inputs
print("\nTesting error handling:")

try:
    ## Invalid expense description
    expenses = add_expense_to_list(expenses, description="", amount=10)
except ValueError as e:
    print(f"Caught error: {e}")

try:
    ## Invalid expense amount
    expenses = add_expense_to_list(expenses, description="Coffee", amount=-5)
except ValueError as e:
    print(f"Caught error: {e}")

try:
    ## Missing required argument
    expenses = add_expense_to_list(expenses, description="Coffee")
except ValueError as e:
    print(f"Caught error: {e}")

Save the file and run:

python3 expense_tracker.py

Expected output:

All Expenses:
1. Groceries: $45.75 (Uncategorized, Not specified)
2. Movie tickets: $25.00 (Entertainment, 2023-11-15)
3. Dinner: $65.40 (Food, 2023-11-14)
4. Gas: $35.80 (Uncategorized, 2023-11-16)

Total expenses: $171.95
Food expenses: $65.40

Expense Summary by Category:
------------------------------
Uncategorized: $81.55
Entertainment: $25.00
Food: $65.40
------------------------------
Total: $171.95

Testing error handling:
Caught error: Description cannot be empty
Caught error: Amount must be positive
Caught error: Expense amount is required

Application Review

Our expense tracker demonstrates several important concepts:

  1. Argument validation: Each function validates its arguments to ensure they meet the expected types and constraints.

  2. Default values: We use default values to make certain arguments optional, like category and date.

  3. Required arguments: For essential information like description and amount, we ensure these are provided and valid.

  4. Keyword arguments: The add_expense_to_list function uses **kwargs to accept expense details in a flexible way.

  5. Error handling: We use appropriate exceptions with meaningful error messages to make debugging easier.

  6. Docstrings: Each function includes a docstring that explains its purpose, arguments, and return values.

By applying these techniques, we've created a robust application that handles function arguments in a reliable and user-friendly way. This approach helps prevent bugs, improves code maintainability, and provides clear feedback when something goes wrong.

Construction d'une application complète

Maintenant que nous avons appris diverses techniques pour gérer et valider les arguments de fonction, appliquons ces compétences pour construire une application simple mais complète. Nous allons créer un système de suivi des dépenses de base qui démontre les bonnes pratiques pour la gestion des arguments de fonction.

Création du fichier de l'application

Créons un nouveau fichier Python pour notre suivi des dépenses :

  1. Cliquez sur le menu "File" dans le WebIDE
  2. Sélectionnez "New File"
  3. Entrez expense_tracker.py comme nom de fichier
  4. Cliquez sur "OK"

Conception des fonctions du suivi des dépenses

Notre suivi des dépenses comportera plusieurs fonctions qui gèrent différents aspects de la gestion des dépenses :

def create_expense(description, amount, category=None, date=None):
    """
    Create a new expense entry.

    Args:
        description (str): Description of the expense
        amount (float): The amount spent
        category (str, optional): Category of the expense
        date (str, optional): The date in YYYY-MM-DD format

    Returns:
        dict: An expense entry
    """
    ## Validate description
    if not isinstance(description, str):
        raise TypeError("Description must be a string")
    if not description:
        raise ValueError("Description cannot be empty")

    ## Validate amount
    if not isinstance(amount, (int, float)):
        raise TypeError("Amount must be a number")
    if amount <= 0:
        raise ValueError("Amount must be positive")

    ## Create the expense dictionary
    expense = {
        "description": description,
        "amount": float(amount),
        "category": category or "Uncategorized",
        "date": date or "Not specified"
    }

    return expense


def add_expense_to_list(expenses, **kwargs):
    """
    Add a new expense to the expenses list.

    Args:
        expenses (list): The list of expenses
        **kwargs: The expense details to be passed to create_expense

    Returns:
        list: The updated list of expenses
    """
    ## Validate the expenses list
    if not isinstance(expenses, list):
        raise TypeError("Expenses must be a list")

    ## Extract required arguments
    if "description" not in kwargs:
        raise ValueError("Expense description is required")
    if "amount" not in kwargs:
        raise ValueError("Expense amount is required")

    ## Create the expense and add it to the list
    expense = create_expense(
        kwargs["description"],
        kwargs["amount"],
        kwargs.get("category"),
        kwargs.get("date")
    )

    expenses.append(expense)
    return expenses


def get_total_expenses(expenses, category=None):
    """
    Calculate the total amount of expenses, optionally filtered by category.

    Args:
        expenses (list): The list of expenses
        category (str, optional): Filter by this category if provided

    Returns:
        float: The total amount
    """
    ## Validate the expenses list
    if not isinstance(expenses, list):
        raise TypeError("Expenses must be a list")

    ## Calculate the total
    if category:
        return sum(e["amount"] for e in expenses if e["category"] == category)
    else:
        return sum(e["amount"] for e in expenses)


def get_expense_summary(expenses):
    """
    Get a summary of expenses by category.

    Args:
        expenses (list): The list of expenses

    Returns:
        dict: A dictionary with categories as keys and total amounts as values
    """
    ## Validate the expenses list
    if not isinstance(expenses, list):
        raise TypeError("Expenses must be a list")

    ## Create the summary
    summary = {}
    for expense in expenses:
        category = expense["category"]
        if category in summary:
            summary[category] += expense["amount"]
        else:
            summary[category] = expense["amount"]

    return summary

Utilisation de notre suivi des dépenses

Utilisons maintenant nos fonctions pour suivre certaines dépenses :

def print_expense_summary(summary):
    """Print a formatted summary of expenses by category."""
    print("\nExpense Summary by Category:")
    print("-" * 30)
    for category, amount in summary.items():
        print(f"{category}: ${amount:.2f}")
    print("-" * 30)
    print(f"Total: ${sum(summary.values()):.2f}")

## Initialize an empty expenses list
expenses = []

## Add some expenses
try:
    ## Add with required arguments only
    expenses = add_expense_to_list(
        expenses,
        description="Groceries",
        amount=45.75
    )

    ## Add with all arguments
    expenses = add_expense_to_list(
        expenses,
        description="Movie tickets",
        amount=25.00,
        category="Entertainment",
        date="2023-11-15"
    )

    ## Add another expense
    expenses = add_expense_to_list(
        expenses,
        description="Dinner",
        amount=65.40,
        category="Food",
        date="2023-11-14"
    )

    ## Add with default category
    expenses = add_expense_to_list(
        expenses,
        description="Gas",
        amount=35.80,
        date="2023-11-16"
    )

    ## Display all expenses
    print("All Expenses:")
    for i, expense in enumerate(expenses, 1):
        print(f"{i}. {expense['description']}: ${expense['amount']:.2f} " +
              f"({expense['category']}, {expense['date']})")

    ## Get and display the total
    total = get_total_expenses(expenses)
    print(f"\nTotal expenses: ${total:.2f}")

    ## Get and display expenses for a specific category
    food_total = get_total_expenses(expenses, "Food")
    print(f"Food expenses: ${food_total:.2f}")

    ## Get and display the summary
    summary = get_expense_summary(expenses)
    print_expense_summary(summary)

except (TypeError, ValueError) as e:
    print(f"Error: {e}")

Ajoutons également du code pour démontrer la gestion des erreurs :

## Try some invalid inputs
print("\nTesting error handling:")

try:
    ## Invalid expense description
    expenses = add_expense_to_list(expenses, description="", amount=10)
except ValueError as e:
    print(f"Caught error: {e}")

try:
    ## Invalid expense amount
    expenses = add_expense_to_list(expenses, description="Coffee", amount=-5)
except ValueError as e:
    print(f"Caught error: {e}")

try:
    ## Missing required argument
    expenses = add_expense_to_list(expenses, description="Coffee")
except ValueError as e:
    print(f"Caught error: {e}")

Enregistrez le fichier et exécutez :

python3 expense_tracker.py

Sortie attendue :

All Expenses:
1. Groceries: $45.75 (Uncategorized, Not specified)
2. Movie tickets: $25.00 (Entertainment, 2023-11-15)
3. Dinner: $65.40 (Food, 2023-11-14)
4. Gas: $35.80 (Uncategorized, 2023-11-16)

Total expenses: $171.95
Food expenses: $65.40

Expense Summary by Category:
------------------------------
Uncategorized: $81.55
Entertainment: $25.00
Food: $65.40
------------------------------
Total: $171.95

Testing error handling:
Caught error: Description cannot be empty
Caught error: Amount must be positive
Caught error: Expense amount is required

Revue de l'application

Notre suivi des dépenses démontre plusieurs concepts importants :

  1. Validation des arguments : Chaque fonction valide ses arguments pour s'assurer qu'ils respectent les types et les contraintes attendus.

  2. Valeurs par défaut : Nous utilisons des valeurs par défaut pour rendre certains arguments facultatifs, comme category et date.

  3. Arguments requis : Pour les informations essentielles comme description et amount, nous nous assurons qu'elles sont fournies et valides.

  4. Arguments nommés : La fonction add_expense_to_list utilise **kwargs pour accepter les détails des dépenses de manière flexible.

  5. Gestion des erreurs : Nous utilisons des exceptions appropriées avec des messages d'erreur significatifs pour faciliter le débogage.

  6. Docstrings : Chaque fonction comprend une docstring qui explique son objectif, ses arguments et ses valeurs de retour.

En appliquant ces techniques, nous avons créé une application robuste qui gère les arguments de fonction de manière fiable et conviviale. Cette approche permet d'éviter les bogues, d'améliorer la maintenabilité du code et de fournir des commentaires clairs en cas de problème.