فهم بنية if __name__ == "__main__" في بايثون: دليل شامل مع أمثلة برمجية

دقائق القراءة: 7

مقدمة إلى if __name__ == "__main__" في بايثون

عندما يقوم مفسّر بايثون بقراءة ملف بايثون، فإنه يقوم أولاً بضبط بعض المتغيرات الخاصة. بعد ذلك، ينفّذ الكود الموجود في الملف. أحد هذه المتغيرات الأساسية هو __name__. يعد فهم هذا المتغير وكيفية تفاعله مع الشرط if __name__ == "__main__" أمراً بالغ الأهمية لأي مطور بايثون يرغب في كتابة كود معياري ومرن.

سيرشدك هذا المقال خطوة بخطوة عبر الأمثلة البرمجية الموضحة، لتتعلم كيفية استخدام هذه البنية ولماذا هي ضرورية جداً في تطوير تطبيقات بايثون الفعالة.

فهم وحدات بايثون وكيفية تعيين المتغير __name__

تُعرف ملفات بايثون باسم الوحدات (modules)، وتُحدّد بامتداد الملف .py. يمكن للوحدة أن تُعرّف دوال (functions)، وفئات (classes)، ومتغيرات. عندما يقوم المفسّر بتشغيل وحدة ما، يتم تعيين المتغير __name__ إلى القيمة __main__ إذا كانت الوحدة قيد التشغيل هي البرنامج الرئيسي.

أما إذا تم استيراد الكود من وحدة أخرى، فسيتم تعيين المتغير __name__ إلى اسم تلك الوحدة المستوردة. دعنا نلقي نظرة على مثال عملي لتوضيح هذا المفهوم بشكل أفضل.

المثال الأول: تشغيل ملف بايثون كبرنامج رئيسي

قم بإنشاء وحدة بايثون باسم file_one.py والصق هذا الكود ذو المستوى الأعلى بداخلها:

# Python file one module
print("File one __name__ is set to: {}".format(__name__))

عند تشغيل هذا الملف مباشرةً من سطر الأوامر، سترى بالضبط ما كنا نتحدث عنه. سيتم تعيين المتغير __name__ لهذه الوحدة إلى __main__، مما يشير إلى أنها نقطة الدخول الرئيسية للبرنامج:

File one __name__ is set to: __main__

المثال الثاني: استيراد وحدة أخرى وتأثيرها على __name__

الآن، أضف ملفاً آخر باسم file_two.py والصق هذا الكود بداخله:

# Python module to import
print("File two __name__ is set to: {}".format(__name__))

ثم، قم بتعديل الكود في file_one.py على النحو التالي لاستيراد الوحدة file_two:

# Python module to execute
import file_two
print("File one __name__ is set to: {}".format(__name__))

عند تشغيل كود file_one مرة أخرى، ستلاحظ أن المتغير __name__ في file_one لم يتغير، ولا يزال معيناً على __main__. لكن الآن، تم تعيين المتغير __name__ في file_two كاسم وحدته، أي file_two. يجب أن تبدو النتيجة كما يلي:

File two __name__ is set to: file_two
File one __name__ is set to: __main__

ولكن إذا قمت بتشغيل file_two مباشرةً، فسترى أن اسمه قد تم تعيينه إلى __main__، مما يؤكد أنه أصبح البرنامج الرئيسي في هذه الحالة:

File two __name__ is set to: __main__

القاعدة الأساسية هنا هي أن المتغير __name__ للملف/الوحدة التي يتم تشغيلها مباشرةً سيكون دائماً __main__. بينما المتغير __name__ لجميع الوحدات الأخرى التي يتم استيرادها سيتم تعيينه إلى اسم وحدتها الفعلية.

الاستخدام العملي لـ if __name__ == "__main__"

الطريقة الشائعة لاستخدام __name__ و __main__ تبدو هكذا:

if __name__ == "__main__":
    # نفّذ شيئاً هنا عندما يتم تشغيل الملف مباشرةً
    # هذا الكود لن يُنفّذ عند استيراد الوحدة

دعنا نرى كيف يعمل هذا النمط في الواقع، وكيف يمكننا استخدام هذه المتغيرات بفعالية للتحكم في تدفق تنفيذ الكود. قم بتعديل file_one و file_two ليبدوا كما يلي:

تعديل file_one.py

# Python module to execute
import file_two
print("File one __name__ is set to: {}".format(__name__))

if __name__ == "__main__":
    print("File one executed when ran directly")
else:
    print("File one executed when imported")

تعديل file_two.py

# Python module to import
print("File two __name__ is set to: {}".format(__name__))

if __name__ == "__main__":
    print("File two executed when ran directly")
else:
    print("File two executed when imported")

مرة أخرى، عند تشغيل file_one، سترى أن البرنامج تعرّف على أي من هاتين الوحدتين هو __main__ ونفّذ الكود وفقاً لعبارات if else الخاصة بنا. يجب أن تبدو النتيجة كما يلي:

File two __name__ is set to: file_two
File two executed when imported
File one __name__ is set to: __main__
File one executed when ran directly

الآن، قم بتشغيل file_two مباشرةً وسترى أن المتغير __name__ قد تم تعيينه إلى __main__، وأن الكتلة الشرطية الخاصة به قد تم تنفيذها:

File two __name__ is set to: __main__
File two executed when ran directly

التحكم في تنفيذ الدوال عند الاستيراد

عندما يتم استيراد وتشغيل وحدات مثل هذه، يتم استيراد دوالها، ويتم تنفيذ الكود ذو المستوى الأعلى (top-level code) مباشرةً. لمعرفة هذه العملية عملياً، قم بتعديل ملفاتك لتبدو كما يلي:

تعديل file_one.py (مع دوال)

# Python module to execute
import file_two
print("File one __name__ is set to: {}".format(__name__))

def function_one():
    print("Function one is executed")

def function_two():
    print("Function two is executed")

if __name__ == "__main__":
    print("File one executed when ran directly")
else:
    print("File one executed when imported")

تعديل file_two.py (مع دوال)

# Python module to import
print("File two __name__ is set to: {}".format(__name__))

def function_three():
    print("Function three is executed")

if __name__ == "__main__":
    print("File two executed when ran directly")
else:
    print("File two executed when imported")

الآن، تم تعريف الدوال وتحميلها في الذاكرة، ولكن لم يتم تشغيلها تلقائياً. لتشغيل إحدى هذه الدوال، قم بتعديل جزء if __name__ == "__main__" من file_one ليبدو كما يلي:

if __name__ == "__main__":
    print("File one executed when ran directly")
    function_two()
else:
    print("File one executed when imported")

عند تشغيل file_one، يجب أن تكون النتيجة كما يلي، حيث يتم استدعاء function_two() فقط عند التشغيل المباشر لـ file_one:

File two __name__ is set to: file_two
File two executed when imported
File one __name__ is set to: __main__
File one executed when ran directly
Function two is executed

يمكنك أيضاً تشغيل دوال من الملفات المستوردة. للقيام بذلك، قم بتعديل جزء if __name__ == "__main__" من file_one ليبدو كما يلي، مع استدعاء دالة من file_two عبر اسمها المؤهل:

if __name__ == "__main__":
    print("File one executed when ran directly")
    function_two()
    file_two.function_three()
else:
    print("File one executed when imported")

ويمكنك توقع نتيجة كهذه، حيث يتم تنفيذ الدالتين من كلا الملفين:

File two __name__ is set to: file_two
File two executed when imported
File one __name__ is set to: __main__
File one executed when ran directly
Function two is executed
Function three is executed

استيراد دوال محددة من الوحدات

لنفترض أن الوحدة file_two كبيرة جداً وتحتوي على العديد من الدوال (دالتين في حالتنا)، ولا ترغب في استيرادها جميعاً لتجنب استهلاك الذاكرة أو تضارب الأسماء. قم بتعديل file_two ليبدو كما يلي:

# Python module to import
print("File two __name__ is set to: {}".format(__name__))

def function_three():
    print("Function three is executed")

def function_four():
    print("Function four is executed")

if __name__ == "__main__":
    print("File two executed when ran directly")
else:
    print("File two executed when imported")

ولاستيراد دوال محددة فقط من الوحدة، استخدم كتلة from import في ملف file_one. هذا يسمح لك بتحديد الدوال التي تحتاجها بالضبط:

# Python module to execute
from file_two import function_three
print("File one __name__ is set to: {}".format(__name__))

def function_one():
    print("Function one is executed")

def function_two():
    print("Function two is executed")

if __name__ == "__main__":
    print("File one executed when ran directly")
    function_two()
    function_three()
else:
    print("File one executed when imported")

بهذه الطريقة، يتم استيراد function_three فقط من file_two، مما يقلل من التبعيات ويجعل الكود أكثر كفاءة ووضوحاً.

الخلاصة

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

يمكننا استخدام كتلة if __name__ == "__main__" للسماح أو منع أجزاء معينة من الكود من التشغيل عند استيراد الوحدات. عندما يقرأ مفسّر بايثون ملفاً، يتم تعيين المتغير __name__ إلى __main__ إذا كانت الوحدة هي التي تُشغل مباشرةً، أو إلى اسم الوحدة إذا تم استيرادها. قراءة الملف تنفّذ جميع الأكواد ذات المستوى الأعلى، ولكن لا تشغّل الدوال والفئات إلا عند استدعائها صراحةً.

الخلاصة التقنية

تُعتبر بنية if __name__ == "__main__" نمط تصميم أساسي في بايثون يعزز من معيارية الكود وقابليته لإعادة الاستخدام. إنها تمكّن المطورين من تضمين كود خاص بالاختبار أو التنفيذ المباشر داخل الوحدة دون التأثير على سلوكها عند استيرادها كوحدة فرعية في مشروع أكبر. هذا الفصل بين منطق التشغيل المباشر ومنطق المكتبة يعزز من نظافة الكود، ويسهل عمليات الاختبار، ويقلل من الآثار الجانبية غير المرغوبة، مما يجعل الوحدات أكثر قوة ومرونة في بيئات التطوير المختلفة ويساهم في بناء أنظمة برمجية متماسكة وقابلة للصيانة.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *