الدوال
نظّم شيفرتك وأعد استخدامها بكفاءة
الدوال في 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
أفضل الممارسات للدوال
مبادئ عامة
-
دالة واحدة، مسؤولية واحدة: ينبغي أن يكون لكل دالة هدف واضح وواحد.
-
أسماء وصفية: اختر أسماءً توضّح ما تفعله الدالة، لا كيف تفعله.
# Bon def calculer_moyenne(nombres): return sum(nombres) / len(nombres) # À éviter def fonction1(liste): return sum(liste) / len(liste) -
حجم معقول: ينبغي أن تكون الدوال قصيرة نسبيًا (عادةً أقل من 20 إلى 30 سطرًا).
-
معاملات افتراضية غير قابلة للتغيير: تجنّب استخدام كائنات قابلة للتغيير (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 -
توثيق واضح: استخدم 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 للمشاركة في النقاش