Menu
العربية

شرح *args و **kwargs في بايثون بالتفصيل

تعرّف على الفرق بين *args و **kwargs في بايثون، ومتى تستخدم كلًا منهما، وكيف تُمرّر الوسائط بين الدوال بأسلوب نظيف واحترافي.

لمّا ما تعرف كم عدد الوسائط اللي راح تستقبلها

أغلب الدوال في بايثون تاخذ عددًا محددًا من الوسائط (parameters). لكن أحيانًا تحتاج شيئًا أكثر مرونة، مثل دالة تسجيل (logger) تقبل أي عدد من الرسائل، أو دالة غلاف (wrapper) تُمرّر كل ما استقبلته إلى دالة أخرى، أو دالة رسم بياني تستقبل إعدادات دون أن تعرف مسبقًا أيّ الخيارات سيستخدمها المستدعي.

بايثون يحل هذه المشكلة عبر علامتين خاصتين داخل قائمة الوسائط: *args و **kwargs.

*args لتجميع الوسائط الموضعية

النجمة المفردة تعني ببساطة: "اجمع أي وسائط موضعية (positional arguments) إضافية داخل tuple":

main.py
Output
Click Run to see the output here.

داخل الدالة، يكون args مجرد tuple عادي. يمكنك المرور عليه بحلقة، أو الوصول لعناصره بالفهرسة، أو تمريره إلى len()، أو تقطيعه بالـ slicing.

عادةً ما يظهر *args بجانب معاملات مسمّاة:

main.py
Output
Click Run to see the output here.

first يمسك أول وسيط، بينما *rest يجمع كل ما يأتي بعده داخل tuple.

**kwargs لتجميع الوسائط المسماة

النجمتان تقومان بنفس الدور لكن مع الوسائط المسماة (keyword arguments)، إذ تُجمَع كلها داخل قاموس dict:

main.py
Output
Click Run to see the output here.

داخل دالة describe، تُعتبر kwargs مجرّد قاموس (dict) عادي، ومفاتيحه هي أسماء الوسائط المُمرَّرة كسلاسل نصية.

الجمع بين args و kwargs في نفس الدالة

تستطيع استخدام الاثنين معًا داخل الدالة الواحدة، والترتيب المتعارف عليه هو وضع *args قبل **kwargs:

main.py
Output
Click Run to see the output here.

الترتيب الكامل للبارامترات، من البداية إلى النهاية، هو كالتالي:

  1. البارامترات الموضعية العادية (سواء كانت إلزامية أو لها قيم افتراضية).
  2. *args.
  3. البارامترات المُمرَّرة بالاسم فقط (أي بارامتر يأتي بعد *args يجب تمريره بالاسم).
  4. **kwargs.
main.py
Output
Click Run to see the output here.

title يمسك أول وسيطة موضعية، وبقية الوسائط الموضعية تذهب كلها إلى tags. أما draft فلا بد من تمريرها كوسيطة مُسمّاة (لأنها جاءت بعد *tags)، وأي وسائط مُسمّاة أخرى تستقر داخل metadata.

فك التعبئة بالنجمة * و** عند استدعاء الدالة

النجمتان تعملان في الاتجاه المعاكس أيضًا — أي نشر عناصر قائمة أو قاموس لتصبح وسائط لاستدعاء دالة:

main.py
Output
Click Run to see the output here.

هالشي مفيد جدًا عند تمرير الوسائط من دالة لأخرى:

main.py
Output
Click Run to see the output here.

لا تحتاج wrapped أن تعرف الوسائط التي تتوقعها log. هي فقط تجمع كل شيء وتمرره كما هو. هذا النمط ستراه كثيراً في الـ decorators (موضوع أعمق قليلاً) وفي دوال التغليف (wrapper functions).

متى يكون استخدام *args و **kwargs خياراً خاطئاً؟

من السهل أن تحمس لهما وتستخدمهما في كل مكان. لكن انتبه لنقطتين:

تُخفيان ما تتوقعه الدالة فعلاً

لو كانت كل دالة في مشروعك بالشكل def f(*args, **kwargs)، فلن يعرف من يستدعيها ما هي الوسائط المقبولة أصلاً. استخدم المعاملات المُسمّاة (named parameters) كلما أمكن، ودع *args/**kwargs تحملان فقط المدخلات المتغيرة العدد فعلياً أو حالات التمرير المباشر (forwarding).

رسائل الخطأ تصبح غامضة

أي خطأ إملائي في اسم وسيط مفتاحي سيتحول إلى قيمة None صامتة أو KeyError في عمق الدالة، بدلاً من رسالة فورية واضحة مثل "unexpected keyword argument" عند موضع الاستدعاء. المعاملات المُسمّاة تعطيك تغذية راجعة أفضل بكثير.

كقاعدة عامة: اجعل المعاملات المُسمّاة هي الخيار الافتراضي، ولا تلجأ إلى *args/**kwargs إلا حين تكون الدالة مرنة فعلاً أو حين تمرّر وسائطها إلى دالة أخرى (callable).

مثال عملي صغير

دالة مساعدة على طريقة مكتبات الرسم، تغلّف دالة خارجية مع بعض القيم الافتراضية:

main.py
Output
Click Run to see the output here.

تُتيح *values لمن يستدعي الدالة تمرير أي عدد من العناصر، بينما تبتلع **style أيّ إعدادات إضافية دون الحاجة لتعريف كل خيار كمعامل مُسمَّى. الأسلوب مرن دون أن يكون غامضاً، لأن منطق الدالة الداخلي يُوضّح لك تماماً أيّ المفاتيح يقرأها من style.

خلاصة الدرس

  • *args تجمع الوسائط الموضعية الإضافية داخل tuple.
  • **kwargs تجمع الوسائط المُسمّاة الإضافية داخل dict.
  • عند استدعاء الدالة، تعمل *seq و **dict في الاتجاه المعاكس، أي فكّ التعبئة بدل جمعها.
  • ترتيب المعاملات لا بد أن يكون: عادية ← *args ← وسائط مُسمّاة حصراً ← **kwargs.
  • لا تُفرط في استخدامها، فالمعاملات المُسمّاة أوضح متى أمكنك الاستغناء عن هذه المرونة.

في الدرس التالي سنتعرّف على lambda، وهي طريقة لكتابة دوال صغيرة سريعة داخل السطر مباشرةً.

الأسئلة الشائعة

ما المقصود بـ *args في بايثون؟

*args يقوم بتجميع أي وسائط موضعية إضافية داخل tuple. فلو عرّفت الدالة بالشكل def f(*args): واستدعيتها بـ f(1, 2, 3)، فستحصل على args بقيمة (1, 2, 3). الاسم args مجرد اصطلاح متعارف عليه، ويمكنك تسميته بأي شيء آخر، لكن *args هو الاسم الذي يستخدمه الجميع في مجتمع بايثون.

وما المقصود بـ **kwargs؟

**kwargs يجمع أي وسائط مفتاحية (keyword arguments) إضافية داخل dict. فلو كتبت def f(**kwargs): واستدعيت الدالة بـ f(name='Ada', age=30)، فستحصل على kwargs بقيمة {'name': 'Ada', 'age': 30}. وباستخدام *args و **kwargs معًا، يمكن للدالة أن تستقبل أي تركيبة ممكنة من الوسائط.

هل يجب أن أسميها args و kwargs تحديدًا؟

لا، المهم هو النجمة * والنجمتان **، وليس الاسم. يمكنك استخدام *values و **options وستعمل بنفس الطريقة تمامًا. لكن args و kwargs أصبحا اصطلاحًا شبه عالمي في أكواد بايثون، لذا يُفضّل الالتزام بهما ما لم يكن هناك سبب واضح لاختيار اسم أكثر وصفًا.

تعلّم البرمجة مع Coddy

ابدأ الآن