مسار بايثون· الخطوة 16 من 19
المعاملات المتعددة في بايثون: دوال تقبل أي عدد من القيم
تعلّم تمرير المعاملات إلى دوال بايثون بالاسم وبالترتيب، وإعطاءها قيمًا افتراضية، واستقبال أي عدد من القيم بـ *args و **kwargs.
في درس الدوال كتبت دالة تجمع نقاط لاعبين اثنين. لكن ماذا لو صار عندك ثلاثة لاعبين؟ أو عشرة؟ هل تكتب دالة جديدة لكل عدد؟ في لغة بايثون هناك طريقة أذكى: دالة واحدة تقبل أي عدد من القيم. في هذا الدرس ستتعلّم كيف تفعل ذلك، وكيف تمرّر القيم بالاسم بدل الترتيب. وكل أكواد بايثون هنا تعمل في محرر بايثون اون لاين بدون أي تثبيت.
🎯 ماذا ستتعلم؟
بنهاية هذا الدرس ستكون قادرًا على:
- تمرير القيم إلى الدالة بالاسم، فلا يهمّ ترتيبها.
- إعطاء المعامل قيمة افتراضية يستخدمها إذا لم تُرسل شيئًا.
- كتابة دالة تقبل أي عدد من القيم باستخدام
*args. - كتابة دالة تقبل أي عدد من القيم المسمّاة باستخدام
**kwargs. - الجمع بين هذه الطرق في دالة واحدة.
🧰 ماذا تحتاج قبل أن تبدأ؟
يجب أن تعرف كيف تعرّف دالة وتستدعيها، وقد شرحناه في درس الدوال. وستحتاج أيضًا القوائم والقواميس. كل الأمثلة تعمل في مترجم بايثون في المتصفح، وهو محرر بايثون اون لاين بالعربي يشغّل بايثون 3 مباشرة.
الفكرة ببساطة
تخيّل أنك تطلب من صديقك أن يشتري لك أغراضًا من البقالة. أحيانًا تعطيه غرضًا واحدًا، وأحيانًا قائمة طويلة. صديقك لا يقول: “أنا أقبل غرضين فقط!”. هو يأخذ القائمة كلها مهما كان طولها.
الدالة في برمجة بايثون تستطيع أن تكون مثل هذا الصديق. بدل أن تحدّد عدد المعاملات (parameters) مسبقًا، تقول لها: “خذي كل ما يصلك”. وتستطيع أيضًا أن تسمّي كل قيمة عند الإرسال، تمامًا كما تكتب على كل كيس اسم ما بداخله.
الخطوة 1: التمرير بالاسم بدل الترتيب
عندما تستدعي دالة، تُرسل القيم عادةً بالترتيب. القيمة الأولى تذهب للمعامل الأول، والثانية للثاني. هذه تسمّى الوسائط الموضعية (positional arguments).
def player_card(name, goals):
print(name, "سجّل", goals, "أهداف")
player_card("سالم", 3)
سالم سجّل 3 أهداف
لكن يمكنك أيضًا أن تكتب اسم المعامل مع القيمة. عندها لا يهمّ الترتيب أبدًا. هذه تسمّى الوسائط المسمّاة (keyword arguments).
def player_card(name, goals):
print(name, "سجّل", goals, "أهداف")
player_card(goals=5, name="نورة")
player_card(name="خالد", goals=1)
نورة سجّل 5 أهداف
خالد سجّل 1 أهداف
لاحظ أننا قلبنا الترتيب في السطر الأول، وبايثون فهمت المطلوب لأن كل قيمة تحمل اسمها. هذا مفيد جدًا مع الدوال التي فيها معاملات كثيرة، فلا تحتاج أن تحفظ ترتيبها.
الخطوة 2: القيم الافتراضية
أحيانًا يكون لمعامل قيمة شائعة لا تتغيّر غالبًا. يمكنك إعطاءه قيمة افتراضية (default value) بعلامة = في تعريف الدالة. إذا لم تُرسل شيئًا، تستخدم الدالة هذه القيمة.
def greet(name, greeting="أهلًا"):
print(greeting, "يا", name)
greet("مريم")
greet("يوسف", "صباح الخير")
greet("هند", greeting="مساء النور")
أهلًا يا مريم
صباح الخير يا يوسف
مساء النور يا هند
في الاستدعاء الأول لم نُرسل تحية، فاستخدمت الدالة “أهلًا”. وفي الثاني والثالث أرسلنا تحية مختلفة بالترتيب أو بالاسم. تذكّر قاعدة مهمة: المعاملات التي لها قيمة افتراضية تأتي بعد المعاملات العادية، لا قبلها.
الخطوة 3: أي عدد من القيم بـ *args
الآن نصل إلى الفكرة الأهم. لو كتبت def total(a, b) فلن تقبل الدالة إلا قيمتين بالضبط. لكن إذا وضعت نجمة * قبل اسم المعامل، فستجمع بايثون كل القيم المرسلة في مجموعة واحدة.
def total_points(*points):
print("وصلني:", points)
result = 0
for p in points:
result = result + p
print("المجموع:", result)
total_points(10, 20)
total_points(5, 15, 25, 35)
وصلني: (10, 20)
المجموع: 30
وصلني: (5, 15, 25, 35)
المجموع: 80
الدالة نفسها قبلت قيمتين ثم أربع قيم بدون أي تغيير. لاحظ شكل الطباعة (10, 20) بأقواس عادية. هذا نوع اسمه الصفّ (tuple)، وهو يشبه القائمة تمامًا لكنه لا يتغيّر بعد إنشائه. تستطيع المرور عليه بحلقة for كما فعلنا.
اسم المعامل points من اختيارك. لكن المبرمجين اعتادوا تسميته args، اختصارًا لكلمة arguments، ولذلك ستقرأ *args كثيرًا في أكواد بايثون على الإنترنت.
ويمكنك وضع معاملات عادية قبل النجمة. القيم الأولى تذهب إليها، والباقي كله يذهب إلى *args:
def team_report(team, *players):
print("فريق", team, "فيه", len(players), "لاعبين:")
for player in players:
print("-", player)
team_report("النجوم", "سالم", "نورة", "خالد")
فريق النجوم فيه 3 لاعبين:
- سالم
- نورة
- خالد
🚀 للمتقدمين: النجمة تعمل في الاتجاه المعاكس أيضًا. إذا كانت عندك قائمة جاهزة، اكتب
total_points(*my_list)وستفكّ بايثون القائمة إلى قيم منفصلة. هذا يسمّى التفكيك (unpacking).
الخطوة 4: أي عدد من القيم المسمّاة بـ **kwargs
ماذا لو أردت أن تستقبل قيمًا مسمّاة لا تعرف أسماءها مسبقًا؟ ضع نجمتين ** قبل اسم المعامل. عندها تجمع بايثون كل الوسائط المسمّاة في قاموس (dictionary) مفاتيحه هي الأسماء.
def show_profile(**info):
print("وصلني:", info)
for key, value in info.items():
print(key, ":", value)
show_profile(name="ليلى", age=13, city="جدة")
وصلني: {'name': 'ليلى', 'age': 13, 'city': 'جدة'}
name : ليلى
age : 13
city : جدة
هذه المرة الطباعة بأقواس معقوفة، لأن info قاموس عادي. تستطيع قراءة أي قيمة منه بمفتاحها، أو المرور عليه بـ items() كما تعلّمت في درس القواميس. والاسم الشائع لهذا المعامل هو kwargs، اختصارًا لـ keyword arguments.
الخطوة 5: نجمع كل شيء في دالة واحدة
يمكنك استخدام الطرق كلها معًا، بشرط أن تحترم الترتيب: المعاملات العادية أولًا، ثم *args، ثم **kwargs. هنا دالة تسجّل طلبًا من مطعم المدرسة:
def order(item, *extras, **options):
print("الطلب:", item)
for extra in extras:
print(" إضافة:", extra)
for key, value in options.items():
print(" ", key, "=", value)
order("شطيرة")
order("بيتزا", "جبن", "زيتون", size="كبيرة", drink="عصير")
الطلب: شطيرة
الطلب: بيتزا
إضافة: جبن
إضافة: زيتون
size = كبيرة
drink = عصير
في الطلب الأول أرسلنا قيمة واحدة فقط، فبقيت extras و options فارغتين ولم تطبع الحلقات شيئًا. وفي الثاني ذهبت “بيتزا” إلى item، والإضافتان إلى extras، والقيمتان المسمّاتان إلى options. دالة واحدة تتعامل مع كل الحالات.
🚀 للمتقدمين: تستطيع فكّ قاموس جاهز في الاستدعاء بنجمتين:
order("بيتزا", **my_options). وتستطيع أيضًا وضع معاملات عادية بعد*args، وعندها يجب إرسال قيمها بالاسم فقط. جرّبdef f(*args, mode):واستدعها بـf(1, 2, mode="x").
⚠️ أخطاء شائعة
هذه الأخطاء يقع فيها كل من يبدأ تعلم بايثون، وكلها سهلة الإصلاح:
1. وسيط موضعي بعد وسيط مسمّى
❌ هذا الكود يتوقف بخطأ
player_card(name="سالم", 3)
SyntaxError: positional argument follows keyword argument
بايثون تشترط أن تأتي القيم بالترتيب أولًا، ثم القيم المسمّاة. إما أن تسمّي الكل، أو تضع الموضعية أولًا:
# ✅ نسمّي كل القيم، أو نضع الموضعية أولًا
def player_card(name, goals):
print(name, "سجّل", goals, "أهداف")
player_card(name="سالم", goals=3)
player_card("سالم", goals=3)
سالم سجّل 3 أهداف
سالم سجّل 3 أهداف
2. إرسال قائمة إلى دالة فيها *args
❌ الكود يعمل لكن النتيجة خطأ
def total_points(*points):
print(len(points))
total_points([10, 20, 30])
1
الدالة استلمت قيمة واحدة فقط، هي القائمة كلها. إذا أردت أن تُعامل عناصر القائمة كقيم منفصلة، فكّها بنجمة:
# ✅ نفكّ القائمة بنجمة عند الاستدعاء
def total_points(*points):
print(len(points))
scores = [10, 20, 30]
total_points(*scores)
3
3. طلب مفتاح غير موجود من kwargs
❌ هذا الكود يتوقف بخطأ
def show_city(**info):
print(info["city"])
show_city(name="ليلى")
KeyError: 'city'
المستخدم لم يُرسل city، فالمفتاح غير موجود في القاموس. استخدم get() مع قيمة بديلة:
# ✅ نستخدم get مع قيمة بديلة
def show_city(**info):
print(info.get("city", "غير معروفة"))
show_city(name="ليلى")
show_city(name="عمر", city="الرياض")
غير معروفة
الرياض
🧪 جرّب بنفسك
افتح مترجم بايثون في المتصفح وجرّب هذه التحديات. إن أخطأت، اقرأ رسالة الخطأ بهدوء، فهي دائمًا تدلّك على السطر.
التحدي 1: متوسط أي عدد من الدرجات
اكتب دالة average تقبل أي عدد من الدرجات وتُرجع متوسطها. تذكّر أن المتوسط هو المجموع مقسومًا على العدد.
def average(*grades):
# احسب المجموع ثم اقسمه على عدد الدرجات
return 0
print(average(80, 90, 100))
0
💡 اضغط لرؤية الحل
def average(*grades):
total = 0
for g in grades:
total = total + g
return total / len(grades)
print(average(80, 90, 100))
print(average(70, 75))
90.0
72.5
التحدي 2: دعوة إلى حفلة
اكتب دالة invite تأخذ اسم صاحب الحفلة أولًا، ثم أي عدد من أسماء الضيوف. اجعل لها معاملًا افتراضيًا day="الجمعة". اطبع سطرًا لكل ضيف بهذا الشكل: “يا هند، يدعوك سالم يوم الجمعة”.
def invite(host, *guests, day="الجمعة"):
# اطبع سطر دعوة لكل ضيف
print(host)
invite("سالم", "هند", "عمر")
سالم
💡 اضغط لرؤية الحل
def invite(host, *guests, day="الجمعة"):
for guest in guests:
print("يا", guest + "،", "يدعوك", host, "يوم", day)
invite("سالم", "هند", "عمر")
invite("نورة", "مريم", day="السبت")
يا هند، يدعوك سالم يوم الجمعة
يا عمر، يدعوك سالم يوم الجمعة
يا مريم، يدعوك نورة يوم السبت
التحدي 3: بطاقة لاعب كاملة
اكتب دالة player تأخذ الاسم، ثم أي عدد من الألعاب التي يجيدها، ثم أي عدد من المعلومات المسمّاة. اطبع الاسم، ثم عدد الألعاب، ثم كل معلومة في سطر.
def player(name, *games, **info):
# اطبع الاسم وعدد الألعاب ثم المعلومات
print(name)
player("فهد", "كرة قدم", "شطرنج", age=14, team="الصقور")
فهد
💡 اضغط لرؤية الحل
def player(name, *games, **info):
print("اللاعب:", name)
print("يجيد", len(games), "ألعاب")
for key, value in info.items():
print(key, ":", value)
player("فهد", "كرة قدم", "شطرنج", age=14, team="الصقور")
اللاعب: فهد
يجيد 2 ألعاب
age : 14
team : الصقور
✅ اختبر نفسك
-
ما نوع المتغيّر
argsداخل دالة معرّفة بـdef f(*args):؟- أ) قائمة (list)
- ب) صفّ (tuple)
- ج) قاموس (dictionary)
-
أيّ استدعاء صحيح لدالة
def f(a, b=2):؟- أ)
f(b=5, 1) - ب)
f(1, b=5) - ج)
f()
- أ)
-
صح أم خطأ:
**kwargsيجمع الوسائط المسمّاة في قاموس مفاتيحه هي أسماء الوسائط.
💡 اضغط لرؤية الإجابات
- ب) صفّ (tuple)، وهو يشبه القائمة لكنه لا يتغيّر.
- ب)
f(1, b=5). الخيار أ يضع وسيطًا موضعيًا بعد مسمّى، والخيار ج لا يُرسل قيمة للمعاملaالإجباري. - صح. كل اسم يصبح مفتاحًا، وكل قيمة تصبح قيمته في القاموس.
📝 الخلاصة
- الوسائط المسمّاة تُرسل بالشكل
name=value، ولا يهمّ ترتيبها. - القيمة الافتراضية تُكتب في تعريف الدالة، وتُستخدم عندما لا تُرسل شيئًا.
*argsيجمع أي عدد من القيم في صفّ.**kwargsيجمع أي عدد من القيم المسمّاة في قاموس.- الترتيب في التعريف: المعاملات العادية، ثم
*args، ثم**kwargs.
هذا ملخص لغة بايثون لهذا الدرس. أوامر لغة بايثون التي تعلّمتها هنا قليلة، لكنها تفتح لك بابًا واسعًا لكتابة دوال مرنة.
⏭️ الخطوة التالية
صرت الآن تكتب دوال تقبل أي شيء يصلها، وهذه مهارة يستخدمها المبرمجون كل يوم. في الدرس القادم ستتعرّف على دوال lambda، وهي دوال قصيرة جدًا تُكتب في سطر واحد. وإلى ذلك الحين، افتح موقع بايثون اونلاين هذا وجرّب أن تعيد كتابة دالة من درس سابق لتقبل أي عدد من القيم. هكذا يكون تعلم لغة بايثون ممتعًا: خطوة صغيرة كل يوم في محرر بايثون أون لاين، بدون أي برنامج بايثون تحتاج إلى تثبيته.