أغرب سطر في بايثون — تفسير مبسّط
لو فتحت أي ملف بايثون تقريبًا، فغالبًا ستجد هذا السطر في آخره:
if __name__ == "__main__":
main()
يبدو الأمر للوهلة الأولى وكأنه طقس سحري متكرر. لكنه في الحقيقة ميزة عملية جدًا حين تفهم وظيفتها، وهي موجودة لأن بايثون لا تفرّق أصلًا بين "السكربتات" و"المكتبات".
الفكرة الأساسية: طريقتان لاستخدام أي ملف بايثون
أي ملف .py في بايثون يمكن التعامل معه بطريقتين مختلفتين:
- كسكربت — تشغّله مباشرةً عبر
python3 file.py. - كموديول — يستورده ملف آخر باستخدام
import file.
اللغة لا تُلزمك بأن تُحدّد مسبقًا أيّهما تكتب. أي ملف قد ينتهي به المطاف يؤدي الدورين معًا.
لنأخذ مثلًا ملفًا باسم math_helpers.py:
# math_helpers.py
def mean(values):
return sum(values) / len(values)
def median(values):
s = sorted(values)
mid = len(s) // 2
if len(s) % 2 == 0:
return (s[mid - 1] + s[mid]) / 2
return s[mid]
# Quick smoke test while developing
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("mean:", mean(numbers))
print("median:", median(numbers))
لو شغّلت هذا الملف مباشرةً، راح يطبع لك نتيجة الاختبار — ممتاز. لكن لو جاء ملف آخر وكتب import math_helpers، فإن جمل print هذي راح تنفّذ أثناء عملية الاستيراد، وكل من يستخدم الموديول راح يشوف مخرجات التجارب اللي كتبتها. وهذا طبعاً مو المطلوب.
ما هو __name__ في بايثون
كل موديول في بايثون يحتوي على متغيّر جاهز اسمه __name__، وبايثون يضبط قيمته تلقائياً:
- عند استيراد الملف، تكون قيمة
__name__هي اسم الموديول المستخدم في الاستيراد — أي"math_helpers"في المثال السابق. - عند تشغيل الملف مباشرةً، يضبط بايثون قيمة
__name__إلى السلسلة الخاصة"__main__".
وهذا بالضبط ما يتيح لك التفريق بين الحالتين. كل ما عليك فعله هو تغليف الكود المخصّص للتشغيل المباشر داخل شرط بسيط:
# math_helpers.py
def mean(values):
return sum(values) / len(values)
def median(values):
s = sorted(values)
mid = len(s) // 2
if len(s) % 2 == 0:
return (s[mid - 1] + s[mid]) / 2
return s[mid]
if __name__ == "__main__":
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("mean:", mean(numbers))
print("median:", median(numbers))
الآن عند تنفيذ الأمر python3 math_helpers.py ستظهر نتائج الاختبار في الطرفية. أما لو استوردت الملف عبر import math_helpers من ملف آخر، فسيتم تجاوز هذه الكتلة بالكامل، وتبقى الدوال متاحة للاستخدام دون تشغيل أي شيء غير مرغوب فيه.
الرفيق الدائم: دالة main() في بايثون
معظم السكربتات الحقيقية يكبر حجمها مع الوقت، ومن الشائع أن نُفرد لها دالة main() مستقلة، مع إبقاء كتلة الحماية مختصرة قدر الإمكان:
# process_orders.py
import sys
def load_orders(path):
with open(path) as f:
return [line.strip() for line in f if line.strip()]
def process(orders):
for order in orders:
print(f"Processing {order}")
def main():
if len(sys.argv) < 2:
print("Usage: python3 process_orders.py <file>")
sys.exit(1)
orders = load_orders(sys.argv[1])
process(orders)
if __name__ == "__main__":
main()
ليه نتكلف ونعمل دالة main() بدل ما نحط المنطق مباشرة تحت جملة if؟
- سهولة الاختبار. تقدر تستورد الموديول وتنادي
main()(أو أي دالة مساعدة تانية) من داخل الاختبارات. - المتغيرات المحلية. أي حاجة بتتعرّف جوه
main()بتبقى متغير محلي، مش اسم على مستوى الموديول. ده بيمنع تضارب الأسماء بالغلط، وبيخلي الواجهة العامة للموديول أنضف. - وضوح الكود. بلوك الحارس (
if __name__ == '__main__') بقى سطرين بس — "نقطة بداية السكربت" واضحة، مش كومة منطق متكدّسة.
عرض عملي سريع
تقدر تشوف السلوك ده بنفسك بشكل مباشر:
عندما تُشغّل المقطع السابق باستخدام بايثون، سيطبع __name__ is: __main__ ثم العبارة "being run as a script." أما إذا قام ملف آخر باستيراد هذا الملف، فسيطبع الفرع الآخر من الشرط.
متى لا تحتاج إلى if __name__ == '__main__'
كثير من ملفات بايثون لا تحتاج أصلاً إلى هذه الحراسة. فالسكربتات القصيرة التي لن يستوردها أحد تعمل بشكل ممتاز بدونها:
# one_off_rename.py
import os
for name in os.listdir("."):
if name.endswith(".txt"):
os.rename(name, name.lower())
هذا تمام تمامًا. أنت لا تبني مكتبة، ولا أحد سيستورد هذا الملف. لكن في اللحظة التي يصبح فيها لديك منطق قابل لإعادة الاستخدام داخل ملف — وتريد أن تحتفظ بكود ذي آثار جانبية بجواره — فحينها تلجأ إلى الحارس (guard).
نقاط الدخول للحزم المثبَّتة
عندما تنشر شيفرتك على هيئة حزمة حقيقية، تتوفر آلية أكثر رسمية: entry_points ضمن بيانات الحزمة (metadata)، وهي تربط أمرًا طرفيًا بدالة معيّنة. فعند تثبيت المستخدم لحزمتك، يحصل على أمر في الـ shell يستدعي الدالة مباشرة. أما إذا كنت تشغّل سكربتات من مجلد على جهازك الشخصي، فإن حارس __main__ هو الأداة القياسية.
خلاصة هذا الفصل
أصبح لديك الآن:
- الدوال لإعطاء اسم للمنطق البرمجي الخاص بك.
*args/**kwargsللاستدعاءات المرنة.- دوال lambda للحالات العابرة التي لا تحتاج اسمًا.
- المزخرفات (decorators) وتلميحات الأنواع (type hints) لتغليف هذه الدوال وتوثيقها.
- الموديولات وpip والبيئات الافتراضية لتقسيم الشيفرة عبر ملفات ومشاريع مختلفة.
- حارس
__main__الذي يجعل السكربتات والموديولات تتعايش في ملف واحد.
التالي: الأصناف (Classes)
حتى الآن كنت في الغالب تقرن الدوال ببيانات بسيطة — قواميس وقوائم وtuples. في الفصل القادم نتعرّف على الأصناف (classes)، وهي طريقة بايثون لربط البيانات بالسلوك في كيان واحد، وهناك ستلتقي بمفاهيم مثل self و__init__ والوراثة والـ dataclasses.
الأسئلة الشائعة
ماذا يفعل السطر if __name__ == '__main__' بالضبط؟
if __name__ == '__main__' بالضبط؟ببساطة، الكود الموجود تحته يُنفَّذ فقط عندما تُشغّل الملف مباشرةً، ولا يُنفَّذ عند استيراده كوحدة (module). هذا يسمح لك بكتابة ملف .py يمكن تشغيله كسكربت أو استيراده للاستفادة من دواله وكلاساته، دون أن يتم تنفيذ كود السكربت تلقائيًا عند الاستيراد.
هل تحتاج بايثون إلى دالة main مثل باقي اللغات؟
main مثل باقي اللغات؟لا، ملفات بايثون تُنفَّذ من الأعلى إلى الأسفل تلقائيًا. لكن بمجرد أن يصبح لديك ملف تريد أن يكون قابلاً للاستيراد أيضًا، يصبح استخدام if __name__ == '__main__' ضروريًا، ومن العادات الشائعة أن تضع منطق السكربت داخل دالة اسمها main() ثم تستدعيها من داخل هذه الحراسة.
ما الفرق بين __name__ و __main__؟
__name__ و __main__؟__name__ هو متغير خاص تضبطه بايثون تلقائيًا في كل وحدة. إذا تم استيراد الملف، فإن __name__ يأخذ اسم الوحدة (مثل 'util'). أما إذا شُغِّل الملف مباشرة، فإن بايثون تضبط قيمة __name__ إلى النص '__main__'. وعبارة if ببساطة تقارن بين الاثنين.