0%
الدوال في Python: تنظيم الشيفرة وإعادة استخدامها

الدوال

نظّم شيفرتك وأعد استخدامها بكفاءة

10-15 دقيقة

الدوال في Python: تنظيم الشيفرة وإعادة استخدامها

تُعدّ الدوال من المفاهيم الأساسية في البرمجة. فهي تتيح تجميع سلاسل من التعليمات في كتل قابلة لإعادة الاستخدام، وتنظيم الشيفرة في مكوّنات منطقية، وتجنّب التكرار. سنستكشف في هذا الدرس الدوال في Python بعمق، بدءًا من الصيغة الأساسية وصولًا إلى المفاهيم المتقدمة.

ما هي الدالة؟

الدالة هي كتلة شيفرة مسمّاة تؤدي مهمة محددة. يمكن للدوال أن تقبل بيانات إدخال (معاملات)، وتجري عمليات على هذه البيانات، وتُرجع نتيجة. وهي تتيح:

  • إعادة استخدام الشيفرة دون الحاجة إلى إعادة كتابتها
  • تبسيط الصيانة عبر عزل المنطق في أقسام مستقلة
  • تحسين قابلية القراءة بتقسيم مشكلة معقدة إلى مشكلات فرعية
  • تسهيل الاختبار عبر عزل سلوكيات محددة

تعريف دالة بسيطة

في Python، تُعرَّف الدوال بالكلمة المفتاحية def متبوعة باسم الدالة وأقواس قد تحتوي على معاملات.

def saluer():
    """Cette fonction affiche un simple message de salutation."""
    print("Bonjour, monde !")

# Appel de la fonction
saluer()  # Affiche: Bonjour, monde !

تشريح الدالة

  • التعريف: يبدأ بـ def، متبوعًا باسم الدالة وأقواس
  • Docstring: سلسلة توثيق (اختيارية لكنها موصى بها) بين علامات اقتباس ثلاثية
  • الجسم: شيفرة مزاحة (indented) تشكّل الدالة
  • التعليمة return: تتيح إرجاع قيمة (اختيارية)

المعاملات والوسائط

تتيح المعاملات للدوال قبول بيانات الإدخال، مما يجعلها أكثر مرونة وقابلية لإعادة الاستخدام.

المعاملات الموضعية

def saluer_personne(prenom, nom):
    """Salue une personne par son nom complet."""
    print(f"Bonjour, {prenom} {nom} !")

# Appel avec des arguments positionnels
saluer_personne("Marie", "Dupont")  # Affiche: Bonjour, Marie Dupont !

المعاملات ذات القيم الافتراضية

تحدد المعاملات الافتراضية قيمة تُستخدم في حال عدم تمرير أي وسيط.

def saluer_avec_formule(nom, formule="Bonjour"):
    """Salue avec une formule personnalisable."""
    print(f"{formule}, {nom} !")

# Appels de la fonction
saluer_avec_formule("Jean")  # Utilise la formule par défaut
# Affiche: Bonjour, Jean !

saluer_avec_formule("Pierre", "Salut")  # Remplace la formule par défaut
# Affiche: Salut, Pierre !

الوسائط المسمّاة

تتيح الوسائط المسمّاة تحديد المعامل الذي يتلقى كل قيمة بشكل صريح.

def decrire_personne(nom, age, profession, ville):
    """Affiche une description d'une personne."""
    print(f"{nom}, {age} ans, est {profession} à {ville}.")

# Appel avec arguments nommés (ordre différent de la définition)
decrire_personne(
    ville="Paris", 
    profession="ingénieure", 
    nom="Sophie", 
    age=32
)
# Affiche: Sophie, 32 ans, est ingénieure à Paris.

عدد متغيّر من الوسائط

تتيح Python تعريف دوال تقبل عددًا متغيرًا من الوسائط.

وسائط موضعية متغيرة (*args)

def calculer_somme(*nombres):
    """Calcule la somme de tous les nombres fournis."""
    resultat = 0
    for nombre in nombres:
        resultat += nombre
    return resultat

# Appels avec différents nombres d'arguments
print(calculer_somme(1, 2))  # 3
print(calculer_somme(1, 2, 3, 4, 5))  # 15
print(calculer_somme())  # 0

وسائط مسمّاة متغيرة (**kwargs)

def afficher_informations(**infos):
    """Affiche toutes les informations fournies sous forme de paires clé-valeur."""
    for cle, valeur in infos.items():
        print(f"{cle}: {valeur}")

# Appel avec plusieurs arguments nommés
afficher_informations(
    nom="Alice", 
    age=28, 
    email="alice@example.com"
)
# Affiche:
# nom: Alice
# age: 28
# email: alice@example.com

الجمع بين أنواع الوسائط

def fonction_complete(arg1, arg2, *args, kwarg1="défaut", **kwargs):
    """Démontre tous les types d'arguments possibles."""
    print(f"arg1: {arg1}")
    print(f"arg2: {arg2}")
    print(f"args: {args}")
    print(f"kwarg1: {kwarg1}")
    print(f"kwargs: {kwargs}")

fonction_complete(1, 2, 3, 4, 5, kwarg1="personnalisé", x=10, y=20)
# Affiche:
# arg1: 1
# arg2: 2
# args: (3, 4, 5)
# kwarg1: personnalisé
# kwargs: {'x': 10, 'y': 20}

القيم المُرجَعة

يمكن للدالة إرجاع قيمة باستخدام التعليمة return. وإذا لم توجد أي تعليمة return، فإن الدالة تُرجع None ضمنيًا.

إرجاع قيمة واحدة

def carre(nombre):
    """Retourne le carré d'un nombre."""
    return nombre ** 2

resultat = carre(4)
print(resultat)  # 16

إرجاع عدة قيم

def obtenir_dimensions():
    """Retourne la largeur et la hauteur d'un écran."""
    largeur = 1920
    hauteur = 1080
    return largeur, hauteur  # Retourne un tuple

dimensions = obtenir_dimensions()
print(dimensions)  # (1920, 1080)

# Déballage du tuple retourné
largeur, hauteur = obtenir_dimensions()
print(f"Largeur: {largeur}, Hauteur: {hauteur}")  # Largeur: 1920, Hauteur: 1080

إرجاع قيم مختلفة حسب الشروط

def analyser_nombre(n):
    """Analyse un nombre et retourne différentes informations selon sa valeur."""
    if n < 0:
        return "négatif"
    elif n == 0:
        return "zéro"
    else:
        return "positif", "pair" if n % 2 == 0 else "impair"

# Appels avec différentes valeurs
print(analyser_nombre(-5))  # négatif
print(analyser_nombre(0))   # zéro
print(analyser_nombre(7))   # ('positif', 'impair')
print(analyser_nombre(8))   # ('positif', 'pair')

الخروج المبكر باستخدام return

def diviser_securise(a, b):
    """Effectue une division sécurisée."""
    if b == 0:
        print("Division par zéro impossible")
        return None  # Sortie anticipée
    return a / b

print(diviser_securise(10, 2))  # 5.0
print(diviser_securise(10, 0))  # None (après avoir affiché le message d'erreur)

نطاق المتغيرات ومساحات الأسماء

يحدد نطاق المتغير الأماكن التي يمكن الوصول إليه منها في شيفرتك.

المتغيرات المحلية والعامة

# Variable globale
x = 10

def modifier_variable():
    # Variable locale
    y = 5
    print(f"Dans la fonction, x = {x}")  # Accès à la variable globale x
    print(f"Dans la fonction, y = {y}")

modifier_variable()
print(f"Hors fonction, x = {x}")
# print(f"Hors fonction, y = {y}")  # Erreur : y n'est pas défini

تعديل متغير عام

لتعديل متغير عام داخل دالة، يجب استخدام الكلمة المفتاحية global.

compteur = 0

def incrementer():
    global compteur  # Déclare que nous utilisons la variable globale
    compteur += 1
    print(f"Compteur = {compteur}")

incrementer()  # Compteur = 1
incrementer()  # Compteur = 2

المتغيرات غير المحلية (closures)

في الدوال المتداخلة، تتيح الكلمة المفتاحية nonlocal الوصول إلى متغيرات الدالة المحيطة.

def creer_compteur():
    """Crée et retourne une fonction de comptage."""
    count = 0
    
    def incrementer():
        nonlocal count  # Accès à la variable de la fonction englobante
        count += 1
        return count
    
    return incrementer

mon_compteur = creer_compteur()
print(mon_compteur())  # 1
print(mon_compteur())  # 2
print(mon_compteur())  # 3

دوال Lambda (الدوال المجهولة)

دوال lambda هي دوال مجهولة تُعرَّف في سطر واحد بصيغة موجزة.

صيغة دوال lambda

# Fonction standard
def carre(x):
    return x ** 2

# Équivalent avec lambda
carre_lambda = lambda x: x ** 2

print(carre(5))        # 25
print(carre_lambda(5)) # 25

الاستخدام النموذجي مع الدوال عالية المستوى

تُستخدم دوال lambda كثيرًا مع دوال مثل map() وfilter() وsorted().

# Utilisation avec map() pour transformer une liste
nombres = [1, 2, 3, 4, 5]
carres = list(map(lambda x: x ** 2, nombres))
print(carres)  # [1, 4, 9, 16, 25]

# Utilisation avec filter() pour filtrer une liste
nombres = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
pairs = list(filter(lambda x: x % 2 == 0, nombres))
print(pairs)  # [2, 4, 6, 8, 10]

# Utilisation avec sorted() pour trier une liste
etudiants = [
    {"nom": "Alice", "note": 85},
    {"nom": "Bob", "note": 92},
    {"nom": "Charlie", "note": 78}
]
etudiants_tries = sorted(etudiants, key=lambda etudiant: etudiant["note"], reverse=True)
for etudiant in etudiants_tries:
    print(f"{etudiant['nom']}: {etudiant['note']}")
# Bob: 92
# Alice: 85
# Charlie: 78

قيود دوال lambda

تقتصر دوال lambda على تعبير واحد، ولا يمكنها احتواء تعليمات متعددة ولا بنى تحكّم معقدة. وللدوال الأكثر تعقيدًا، يُفضَّل استخدام صيغة def.

الدوال العودية

الدالة العودية هي دالة تستدعي نفسها.

مثال أساسي: العاملي

def factorielle(n):
    """Calcule la factorielle de n de manière récursive."""
    if n <= 1:  # Cas de base
        return 1
    else:  # Appel récursif
        return n * factorielle(n - 1)

print(factorielle(5))  # 120 (5 * 4 * 3 * 2 * 1)

مثال متقدم: اجتياز بنية متداخلة

def parcourir_structure(structure, niveau=0):
    """Parcourt récursivement une structure de données imbriquée."""
    # Afficher la structure actuelle avec indentation selon le niveau
    indentation = "  " * niveau
    print(f"{indentation}Niveau {niveau}: {structure}")
    
    # Si la structure est un dictionnaire ou une liste, parcourir récursivement
    if isinstance(structure, dict):
        for cle, valeur in structure.items():
            print(f"{indentation}  Clé: {cle}")
            parcourir_structure(valeur, niveau + 1)
    elif isinstance(structure, list):
        for i, item in enumerate(structure):
            print(f"{indentation}  Index: {i}")
            parcourir_structure(item, niveau + 1)

# Exemple d'utilisation
structure_exemple = {
    "personne": {
        "nom": "Dupont",
        "adresses": [
            {"type": "domicile", "ville": "Paris"},
            {"type": "travail", "ville": "Lyon"}
        ]
    }
}

parcourir_structure(structure_exemple)

حدود العودية

تضع Python حدًا لعمق العودية لتجنّب فيض المكدّس (stack overflow). وهذا الحد افتراضيًا هو عادةً 1000 استدعاء.

import sys
print(sys.getrecursionlimit())  # Affiche la limite actuelle (généralement 1000)

# Pour modifier la limite (à utiliser avec précaution)
# sys.setrecursionlimit(2000)

لتجنّب مشكلات العودية العميقة، يمكن إعادة كتابة بعض الخوارزميات العودية بشكل تكراري أو باستخدام تقنيات مثل الحفظ المؤقت للنتائج (memoization).

مُزخرِفات الدوال (Decorators)

المُزخرِفات (decorators) مفهوم متقدم يتيح تعديل سلوك الدوال أو توسيعه.

المفهوم الأساسي

def mon_decorateur(fonction):
    def wrapper():
        print("Avant l'appel de la fonction")
        fonction()
        print("Après l'appel de la fonction")
    return wrapper

@mon_decorateur
def dire_bonjour():
    print("Bonjour !")

dire_bonjour()
# Affiche:
# Avant l'appel de la fonction
# Bonjour !
# Après l'appel de la fonction

مُزخرِف يحافظ على الوسائط

def decorateur_avec_arguments(fonction):
    def wrapper(*args, **kwargs):
        print(f"Arguments: {args}, Kwargs: {kwargs}")
        resultat = fonction(*args, **kwargs)
        print(f"Résultat: {resultat}")
        return resultat
    return wrapper

@decorateur_avec_arguments
def addition(a, b):
    return a + b

addition(3, 5)
# Affiche:
# Arguments: (3, 5), Kwargs: {}
# Résultat: 8

مُزخرِفات ذات معاملات

def repeter(n):
    def decorateur(fonction):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                resultat = fonction(*args, **kwargs)
            return resultat
        return wrapper
    return decorateur

@repeter(3)
def dire_bonjour(nom):
    print(f"Bonjour, {nom} !")
    return nom

dire_bonjour("Alice")
# Affiche 3 fois:
# Bonjour, Alice !

التوثيق وتوصيفات الأنواع

Docstrings

تتيح الـ docstrings (سلاسل التوثيق) توثيق دوالك.

def calculer_moyenne(nombres):
    """
    Calcule la moyenne arithmétique d'une liste de nombres.
    
    Args:
        nombres (list): Une liste de nombres (int ou float)
        
    Returns:
        float: La moyenne des nombres
        
    Raises:
        ZeroDivisionError: Si la liste est vide
        
    Examples:
        >>> calculer_moyenne([1, 2, 3, 4, 5])
        3.0
    """
    return sum(nombres) / len(nombres)

يمكن الاطلاع على الـ docstrings باستخدام الدالة help():

help(calculer_moyenne)

توصيفات الأنواع (Python 3.5+)

تحسّن توصيفات الأنواع قابلية القراءة وتتيح التحقق الساكن من الشيفرة.

def saluer(nom: str, age: int = 30) -> str:
    """Retourne un message de salutation personnalisé."""
    return f"Bonjour {nom}, vous avez {age} ans !"

# Les annotations sont stockées dans l'attribut __annotations__
print(saluer.__annotations__)
# {'nom': <class 'str'>, 'age': <class 'int'>, 'return': <class 'str'>}

للأنواع الأكثر تعقيدًا، استخدم الوحدة typing:

from typing import List, Dict, Tuple, Optional, Union, Callable

def traiter_donnees(
    valeurs: List[int],
    config: Dict[str, str],
    limites: Tuple[int, int],
    callback: Optional[Callable[[int], bool]] = None
) -> Union[int, str]:
    """Traite des données avec des types complexes."""
    # Implémentation...
    return 0

أفضل الممارسات للدوال

مبادئ عامة

  1. دالة واحدة، مسؤولية واحدة: ينبغي أن يكون لكل دالة هدف واضح وواحد.

  2. أسماء وصفية: اختر أسماءً توضّح ما تفعله الدالة، لا كيف تفعله.

    # Bon
    def calculer_moyenne(nombres):
        return sum(nombres) / len(nombres)
    
    # À éviter
    def fonction1(liste):
        return sum(liste) / len(liste)
  3. حجم معقول: ينبغي أن تكون الدوال قصيرة نسبيًا (عادةً أقل من 20 إلى 30 سطرًا).

  4. معاملات افتراضية غير قابلة للتغيير: تجنّب استخدام كائنات قابلة للتغيير (mutable) كقيم افتراضية.

    # Problématique
    def ajouter_element(element, liste=[]):
        liste.append(element)
        return liste
    
    # Meilleure approche
    def ajouter_element(element, liste=None):
        if liste is None:
            liste = []
        liste.append(element)
        return liste
  5. توثيق واضح: استخدم docstrings لشرح الغرض من الدالة ومعاملاتها وقيم الإرجاع.

اصطلاحات التسمية

اتبع PEP 8 في اصطلاحات التسمية:

  • استخدم أسماء بأحرف صغيرة تفصل بينها شرطات سفلية (snake_case)
  • فضّل الأفعال لأسماء الدوال التي تنفّذ إجراءات
  • حافظ على الاتساق في اصطلاحاتك
# Bon
def calculer_moyenne(nombres):
    pass

def obtenir_utilisateur_par_id(user_id):
    pass

def est_valide(donnee):
    pass

مثال كامل: تطبيق الدوال

إليك مثالًا يجمع بين عدة مفاهيم متعلقة بالدوال لإنشاء نظام بسيط لإدارة المهام:

from typing import List, Dict, Optional, Tuple, Callable
import time

# Types personnalisés
Tache = Dict[str, any]
ListeTaches = List[Tache]

def creer_tache(
    titre: str, 
    description: str = "", 
    priorite: int = 1
) -> Tache:
    """
    Crée une nouvelle tâche avec un horodatage automatique.
    
    Args:
        titre: Le titre de la tâche
        description: Une description détaillée (optionnelle)
        priorite: Niveau de priorité (1-5, 5 étant le plus important)
        
    Returns:
        Un dictionnaire représentant la tâche
    """
    return {
        "id": int(time.time()),
        "titre": titre,
        "description": description,
        "priorite": max(1, min(5, priorite)),  # Garantit que la priorité est entre 1 et 5
        "termine": False,
        "date_creation": time.strftime("%Y-%m-%d %H:%M:%S")
    }

def ajouter_tache(
    liste_taches: ListeTaches, 
    tache: Tache
) -> ListeTaches:
    """Ajoute une tâche à la liste et retourne la liste mise à jour."""
    liste_taches.append(tache)
    return liste_taches

def marquer_comme_termine(
    liste_taches: ListeTaches, 
    tache_id: int
) -> Tuple[bool, str]:
    """
    Marque une tâche comme terminée.
    
    Returns:
        Un tuple (succès, message)
    """
    for tache in liste_taches:
        if tache["id"] == tache_id:
            tache["termine"] = True
            return True, f"Tâche '{tache['titre']}' marquée comme terminée"
    return False, "Tâche non trouvée"

def filtrer_taches(
    liste_taches: ListeTaches, 
    termine: Optional[bool] = None, 
    priorite_min: int = 0
) -> ListeTaches:
    """
    Filtre les tâches selon plusieurs critères.
    
    Args:
        liste_taches: Liste des tâches à filtrer
        termine: Si spécifié, filtre les tâches selon leur état
        priorite_min: Filtre les tâches avec une priorité supérieure ou égale
        
    Returns:
        Une nouvelle liste contenant les tâches filtrées
    """
    resultat = []
    for tache in liste_taches:
        # Vérifier l'état de la tâche si le paramètre est spécifié
        if termine is not None and tache["termine"] != termine:
            continue
        
        # Vérifier la priorité minimale
        if tache["priorite"] < priorite_min:
            continue
            
        resultat.append(tache)
    return resultat

def afficher_taches(
    liste_taches: ListeTaches,
    formatter: Callable[[Tache], str] = None
) -> None:
    """
    Affiche une liste de tâches.
    
    Args:
        liste_taches: Liste des tâches à afficher
        formatter: Fonction optionnelle pour formater chaque tâche
    """
    if not liste_taches:
        print("Aucune tâche à afficher.")
        return
    
    # Formatter par défaut
    if formatter is None:
        def formatter(tache):
            statut = "✓" if tache["termine"] else "☐"
            return f"{statut} [{tache['priorite']}] {tache['titre']}"
    
    # Afficher chaque tâche
    for i, tache in enumerate(liste_taches, 1):
        print(f"{i}. {formatter(tache)}")

# Utilisation de nos fonctions
if __name__ == "__main__":
    mes_taches = []
    
    # Ajouter quelques tâches
    mes_taches = ajouter_tache(
        mes_taches, 
        creer_tache("Apprendre les fonctions Python", "Tutoriel complet", 4)
    )
    mes_taches = ajouter_tache(
        mes_taches, 
        creer_tache("Faire les courses")
    )
    mes_taches = ajouter_tache(
        mes_taches, 
        creer_tache("Répondre aux emails", priorite=5)
    )
    
    # Afficher les tâches
    print("Toutes les tâches:")
    afficher_taches(mes_taches)
    
    # Marquer une tâche comme terminée
    succes, message = marquer_comme_termine(mes_taches, mes_taches[1]["id"])
    print(f"\nRésultat: {message}")
    
    # Filtrer et afficher les tâches non terminées avec priorité >= 4
    taches_importantes = filtrer_taches(mes_taches, termine=False, priorite_min=4)
    print("\nTâches importantes non terminées:")
    afficher_taches(taches_importantes)
    
    # Utiliser un formateur personnalisé
    print("\nAffichage détaillé:")
    afficher_taches(mes_taches, lambda t: f"{t['titre']} - Priorité: {t['priorite']} - Créée le: {t['date_creation']}")

الخاتمة

تُعدّ الدوال من أهم ركائز البرمجة بلغة Python. فهي تتيح لك تنظيم شيفرتك وجعلها أكثر modularity ووضوحًا وأسهل صيانةً. وبإتقان التقنيات المختلفة المعروضة في هذا الدرس، ستتمكن من:

  • تقسيم المشكلات المعقدة إلى مشكلات فرعية يسهل التعامل معها
  • تجنّب تكرار الشيفرة
  • إنشاء مكتبات من الدوال القابلة لإعادة الاستخدام
  • كتابة شيفرة أكثر تعبيرًا وأسهل صيانةً

في الدرس القادم، سنستكشف الوحدات (modules) والحزم (packages) في Python، التي ستتيح لك تنظيم دوالك وأصنافك في وحدات منطقية قابلة لإعادة الاستخدام.


هل تحتاج إلى مراجعة هياكل البيانات؟ اطّلع على درسنا حول هياكل البيانات في Python.

التعليقات

التعليقات مدعومة عبر GitHub Discussions

سجّل الدخول باستخدام GitHub للمشاركة في النقاش

تم نسخ الرابط!