برامج الكمبيوتر هي ببساطة مجموعات من التعليمات. لإجراء بعض العمليات الحسابية الأساسية وحفظ الناتج، نحتاج إلى شيء مثل التعليمات التالية: “تحميل الرقم 8″، ثم “تحميل الرقم 9″، ثم “إضافة الرقم الأول إلى الرقم الثاني وتخزينه في مكان ما”. لكن البرامج تُكتب عادةً على مستوى أعلى في أكواد يمكن قراءتها من قبل البشر مثل بايثون. فكيف ينتقل الكمبيوتر من هذا الكود القابل للقراءة من قبل البشر إلى قائمة من التعليمات التي يمكنه تنفيذها؟ هنا يأتي دور مفسّر بايثون.
لا تحتاج بالضرورة إلى معرفة أي شيء في هذه المقالة لكي تصبح مبرمجًا ناجحًا في بايثون، ولكن من المثير للاهتمام مع ذلك معرفة كيفية قيام بايثون بتنفيذ التعليمات البرمجية التي تكتبها. ومع ذلك، قد يكون من المفيد من الناحية العملية فهم كيفية تشغيل برامجك ولماذا قد تكون بعض التطبيقات أسرع من غيرها. سنعرض لك مثالاً جيدًا لتوضيح ذلك لاحقًا.
قبل أن تتعمق أكثر هنا، نوصيك بقراءة مصطلحات بايثون التي يجب على المبتدئين معرفتها – الجزء الأول، حيث نعرف بعض المصطلحات المهمة.
كيف يتم تشغيل البرامج
يوجد داخل جهاز الكمبيوتر وحدة معالجة مركزية (CPU) وذاكرة على هيئة ذاكرة وصول عشوائي (RAM). تخزن ذاكرة الوصول العشوائي (RAM) مؤقتًا تعليمات البرنامج، ثم تقوم وحدة المعالجة المركزية بجلبها وتنفيذها. تُعرف التعليمات المخزنة في ذاكرة الوصول العشوائي (RAM) باسم التعليمات البرمجية الآلية وتعتمد على المعالج (أي أن التعليمات البرمجية الآلية ستكون مختلفة باختلاف المعالجات).
يتم تخزين الكود المصدري للغة بايثون الذي تكتبه كملف نصي على القرص الصلب. ومع ذلك، فإن وحدة المعالجة المركزية في جهاز الكمبيوتر لا تفهم لغة بايثون – أو أي لغة برمجة أخرى، في هذا الصدد. إنها تفهم فقط الكود الآلي، والذي يتم تمثيله بأرقام ثنائية. لذا، فإن المشكلة تتلخص في ترجمة الكود القابل للقراءة من قبل البشر إلى الكود الآلي الذي يمكن للمعالج فهمه ثم تنفيذه.
اللغات المترجمة مقابل اللغات المفسرة
هناك طريقتان لحل مشكلة ترجمة الكود الذي نكتبه إلى مجموعة من التعليمات التي يمكن للمعالج فهمها وتنفيذها. ويمكننا توضيح ذلك باستخدام تشبيه. لنفترض أنك قارئ نهم في مزاج لقراءة بعض الأدبيات التي تحير العقل من تأليف فرانز كافكا. ولكن إذا كنت لا تستطيع فهم اللغة الألمانية، فلن يوصلك كتاب مكتوب باللغة الألمانية إلى أي شيء. والخياران المتاحان أمامك هما أن تجد شخصًا يترجم الكتاب بالكامل إلى اللغة الإنجليزية أو أن تطلب من صديق يتحدث الألمانية أن يقرأ النسخة الألمانية سطرًا بسطر ويترجمها شفهيًا (أو يفسرها) إلى اللغة الإنجليزية نيابة عنك.
وينطبق الأمر نفسه على أجهزة الكمبيوتر. فاللغات المترجمة مثل C أو C++ تتبع النهج الأول. والمترجم في القياس أعلاه هو برنامج كمبيوتر يسمى المترجم. يأخذ المترجم الكود المصدري الذي تكتبه كمدخل ويترجمه كله إلى تعليمات في كود الآلة.
تتبع اللغات المفسرة النهج الثاني. فبينما يتم تشغيل البرنامج، يقرأ المترجم الكود المصدر سطرًا بسطر ويترجمه إلى تعليمات للمعالج.
إن عملية ترجمة الكود بواسطة مترجم أثناء وقت التشغيل تخلق بعض التكاليف الحسابية. لذلك، تكون البرامج المفسرة أبطأ عادةً من البرامج المترجمة مباشرةً إلى كود الآلة. من ناحية أخرى، يحتاج الكود المترجم إلى الترجمة لمعالج معين وبالتالي فهو يعتمد على النظام الأساسي.
إذن، هل يتم تجميع بايثون أم تفسيره؟ مثل معظم الأشياء، الأمر معقد بعض الشيء. يتم تجميع بايثون وتفسيره، اعتمادًا على كيفية استخدامه. يمكن تشغيله كلغة مفسرة في الوضع التفاعلي، أو يمكنه تجميع الكود المصدر إلى ملف ثنائي ثم تنفيذ الملف الثنائي. سنتناول التفاصيل أدناه.
مُفسِّر بايثون
لنبدأ بالافتراض أنك قمت بالفعل بتنزيل بايثون وتثبيته وأن لديك نصًا جاهزًا للاستخدام. عندما تريد تشغيل نص يسمى “script.py” من موجه الأوامر، فأنت بحاجة أولاً إلى فتح موجه الأوامر والانتقال إلى الدليل حيث يتم حفظ النص. ثم يمكنك كتابة …
python script.py
يؤدي كتابة كلمة بايثون في موجه الأوامر إلى استدعاء مُفسِّر بايثون، وهو البرنامج الذي تقوم بتشغيله بالفعل. فهو ببساطة يأخذ النص البرمجي (كحجة) ليتم معالجته بشكل أكبر.
يتألف مُفسِّر بايثون نفسه من جزأين: المُجمِّع والآلة الافتراضية لبايثون (PVM). يقوم المُجمِّع بما تقوم به المُجمِّعات؛ فهو يترجم الكود المصدر في script.py إلى شيء ما. ومع ذلك، فإن هذا الشيء ليس كودًا آليًا. إنه تنسيق وسيط يسمى البايت كود، والذي يحدث أيضًا أن يتم تمثيله بتنسيق ثنائي. إذا لاحظت وجود بعض ملفات .pyc العائمة في دليل المشروع، فهذا هو البايت كود. لا يمكن للمعالج فهمه وبالتالي فهو لا يعتمد على المعالج. من المفترض أن يفهمه المكون الثاني من مُفسِّر بايثون – الآلة الافتراضية لبايثون.
آلة بايثون الافتراضية هي قطعة من البرامج تؤدي وظيفة مماثلة للمعالج المادي. فهي تقرأ الكود الثنائي وتنفذ التعليمات.
كيفية استخدام مُفسِّر بايثون
سنستكشف طريقتين لاستخدام مترجم بايثون: أولاً تشغيله بشكل تفاعلي وثانيًا تمرير نص برمجي له بلغة بايثون. لفتح مترجم بايثون في الوضع التفاعلي، افتح موجه الأوامر واكتب:
python
بعد الضغط على Enter، يقوم المترجم بطباعة رسالة ترحيب تحتوي على بعض معلومات النظام ومعلومات إصدار بايثون. من هنا، يمكنك ببساطة البدء في كتابة برنامجك حيث ترى (>>>
). حاول طباعة “Hello, World!”.
بالنسبة للكود متعدد الأسطر – على سبيل المثال حلقة for – يبدأ الكود من موجه ثانوي (…
). لتنفيذ الكود، كل ما عليك فعله هو الضغط على Enter.
في الوضع التفاعلي، يتم تنفيذ الكود على الفور، مع عرض النتائج مباشرة أدناه. لمزيد من المعلومات والأمثلة، راجع وثائق بايثون حول استخدام مفسّر بايثون. بمجرد الانتهاء من العمل في الوضع التفاعلي، اكتب exit()
أو quit()
للعودة إلى موجه الأوامر.
بالنسبة للطريقة الثانية، ستحتاج إلى إنشاء نص برمجي بسيط مثل برنامج hello world. ما عليك سوى فتح محرر نصوص وكتابة:
print('Hello, World!')
ثم احفظ هذا باسم script.py. لتشغيل هذا البرنامج النصي، ما عليك سوى فتح موجه الأوامر أو الطرفية وكتابة:
python script.py
بعد الضغط على Enter لتنفيذ البرنامج، يجب أن ترى الناتج مطبوعًا مباشرة على موجه الأوامر. لاحظ فقط أنه على الرغم من أنه من الممكن كتابة البرامج وتنفيذها بهذه الطريقة، إلا أنها ليست الطريقة الأكثر ملاءمة. يستخدم المبرمجون في الغالب بيئات التطوير المتكاملة لكتابة البرامج وتشغيلها. لدينا مقال عن أفضل 4 بيئات تطوير متكاملة ومحرري أكواد بايثون مع مزيد من التفاصيل.
نظرة أقرب على البايت كود
في المقدمة، وعدنا بأن كل هذا الحديث عن المترجمين وتجميع التعليمات البرمجية يمكن أن يكون مفيدًا عمليًا. لذا، دعنا نفحص الكود الثنائي لوظيفتين ونرى التعليمات التي يتم تمريرها إلى آلة بايثون الافتراضية للتنفيذ. توجد هذه التعليمات في ملفات .pyc الثنائية التي ذكرناها أعلاه. لا يمكننا فهم الكود الثنائي، ولكن يمكننا الحصول على نسخة قابلة للقراءة من التعليمات باستخدام وحدة dis. يتيح لنا هذا معرفة ما يفعله بايثون “تحت الغطاء” عندما يقوم بتشغيل التعليمات البرمجية الخاصة بنا.
يمكن تحديد ذلك في موجه الأوامر عند استدعاء مفسر بايثون:
python -m dis script.py
في هذا التمرين، نريد مقارنة تنفيذين مختلفين قليلاً لنفس الوظيفة. في مُفسِّر بايثون، يمكننا تعريف وظائفنا التي تضيف ببساطة رقمين:
>>> def add_numbers_v1():
... return 8 + 9
>>> def add_numbers_v2():
... a, b = 8, 9
... return a + b
إذا قمت بتوقيت هذه الوظائف، فستلاحظ أن التنفيذ الثاني أبطأ كثيرًا. يمكننا فهم السبب من خلال استيراد وحدة dis
لتفكيك الوظيفتين وفحص التعليمات:
>>> import dis
>>> dis.dis(add_numbers_v1)
2 0 LOAD_CONST 1 (17)
2 RETURN_VALUE
>>> dis.dis(add_numbers_v2)
2 0 LOAD_CONST 1 ((8, 9))
2 UNPACK_SEQUENCE 2
4 STORE_FAST 0 (a)
6 STORE_FAST 1 (b)
3 8 LOAD_FAST 0 (a)
10 LOAD_FAST 1 (b)
12 BINARY_ADD
14 RETURN_VALUE
كل سطر في الإخراج عبارة عن تعليمة. تحتوي النسخة الأولى والأسرع على تعليمتين فقط، مقارنة بالتعليمات الثمانية للتنفيذ الثاني. وهذا يفسر الفرق في وقت التنفيذ. إذا وجدت نفسك تتساءل عما يفعله بايثون خلف الكواليس، فانظر إلى الكود الثنائي للحصول على بعض الأفكار.
لقد قمنا بتغطية الكثير من المواد المفاهيمية في هذه المقالة، مما يمنحك نظرة عامة على مفسّر بايثون مع بعض الأمثلة العملية.
الشيء الوحيد المتبقي الآن هو تعلم بايثون. فهي في النهاية واحدة من أكثر لغات البرمجة شيوعًا وهي مناسبة للمبتدئين.
اكتشاف المزيد من بايثون العربي
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.