اذهب إلى المحتوى

مصطلحات شائعة مثيرة للالتباس في بايثون


محمد الخضور

المصطلحات التقنية مربكة كفاية، فما بالك بمصطلحات متعلقة ببعضها وذات تعاريف مختلفة بنفس الوقت، وما يجعل الأمور تزداد سوءًا أن لغات البرمجة وأنظمة التشغيل ومجالات الحوسبة عمومًا قد تستخدم مصطلحات مختلفة لتوصيف الشيء ذاته أو تستخدم نفس المصطلحات لتوصيف أشياء مختلفة. لا بُد من التمييز ما بين المصطلحات التالية بما يضمن لك التواصل الواضح مع المبرمجين الآخرين.

سنتعرّف في هذا المقال على مجموعة من المصطلحات التي قد تثير الالتباس في فهمها لنتجنّب الخلط فيما بينها.

التعليمات البرمجية Statements والتعابير البرمجية Expressions

التعابير البرمجية هي تعليمات مكونة من عوامل وقيمٍ والتي تُقيّم إلى قيمة وحيدة، ويمكن لهذه القيمة أن تكون متغيرًا (يحتوي على قيمة) أو استدعاء دالة (يعيد قيمة)؛ فمثلًا 2+2 هو تعبير برمجي إذ يُقيّم إلى القيمة الوحيدة 4، كما أن كلًا من len(myName) > 4 و ()myName.isupper أو 'myName == 'Zophie هي تعابير برمجية أيضًا. تُعد القيمة الوحيدة بحد ذاتها تعبيرًا برمجيًا أيضًا، إذ تُقيّم إلى نفسها.

أما التعليمات البرمجية فهي كل ما تبقى من أوامر في بايثون (باستثناء التعابير البرمجية)، والتي تتضمّن تعليمات الجمل الشرطية if والحلقات التكرارية for وتعليمات التصريح عن الدوال def وتعليمات القيم المعادة return وغيرها، إذ لا تُقيّم التعليمات البرمجية إلى قيمة وحيدة، وقد تحتوي بعض التعليمات البرمجية على تعابير برمجية، كما في تعليمات الإسناد مثل spam = 2 + 2 أو تعليمات الشرط مثل 'myName == 'Zophie.

يستخدم الإصدار الثالث من بايثون الدالة ()print، بينما يستخدم الإصدار الثاني منها التعليمة print بدلًا عنها، وقد يبدو أن الفرق بينهما هو فقط استخدام الأقواس أو عدمه، ولكن من المهم ملاحظة أن الدالة ()print في الإصدار الثالث من بايثون تتضمّن قيمة معادة (وهي تساوي دومًا None) كما من الممكن تمريرها وسيطًا للدوال الأخرى ويمكن إسنادها إلى المتغيرات، في حين أن كل من الإجراءات السابقة غير متاح لدى التعامل مع التعليمات. مع ذلك، يمكن استخدام الأقواس مع تعليمة print في الإصدار الثاني من بايثون كما في المثال التالي:

>>> print 'Hello, world!' # run in Python 2
Hello, world!
1 >>> print('Hello, world!') # run in Python 2
Hello, world!

يبدو السطر ذو الرقم 1 مثل استدعاء دالة، إلا أنه تعليمة print مع سلسلة نصية محصورة بين قوسين، وكذلك تعليمة الإسناد (spam = (2 + 2 تكافئ spam = 2 + 2. يمكن في كل من الإصدارين 2 و 3 من بايثون تمرير قيم متعددة إلى التعليمة print أو الدالة ()print على التوالي. سيبدو الأمر في الإصدار 3 من يابثون بالشكل التالي:

>>> print('Hello', 'world') # run in Python 3
Hello world

لو استخدمنا نفس الشيفرة السابقة ولكن في الإصدار الثاني من بايثون، ستفسّر التعليمة على أنها تمرير لصف مكون من سلسلتين نصيتين ضمن تعليمة print، لينتج الخرج التالي:

>>> print('Hello', 'world') # run in Python 2
('Hello', 'world')

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

الكتل Block والبنية Clause والمتن Body

تُستخدم عادةً كل من مصطلحات الكتلة والبنية والمتن تبادليًا للإشارة إلى مجموعة من تعليمات بايثون. تبدأ الكتل بمسافة بادئة وتنتهي عندما تصبح المسافة البادئة بمستوى تلك السابقة، فعلى سبيل المثال، تدعى الشيفرات التالية لتعليمة if أو لتعليمة for بكتلة التعليمات، ولا بُد من وجود كتلة جديدة بعد التعليمات المنتهية بنقطتين رأسيتين :، مثل تعليمات if و else و for و while و def و class وغيرها.

تسمح بايثون بكتل السطر الواحد، فهي تعمل رغم كونها غير منصوح بها في صيغة بايثون:

if name == 'Zophie': print('Hello, kitty!')

ومع استخدام الفاصلة المنقوطة، يصبح من الممكن استخدام عدة أوامر ضمن كتلة التعليمة if، على النحو التالي:

if name == 'Zophie': print('Hello, kitty!'); print('Do you want a treat?')

لكن من غير المسموح استخدام نفس السطر لكتابة تعليمات أخرى تتطلب بدورها كتلًا جديدة، فالشيفرة التالية مثلًا غير صالحة في بايثون:

if name == 'Zophie': if age < 2: print('Hello, kitten!')

وسبب رفض هذه الصيغة هو في حال وجود تعليمة else في السطر التالي، سيكون من الصعب معرفة كونها تابعة لأي من تعليمتي if.

يفضّل توثيق بايثون الرسمي استخدام مصطلح البنية بدلًا من الكتلة، والشيفرة التالية تمثّل بنية:

if name == 'Zophie':
    print('Hello, kitty!')
    print('Do you want a treat?')

تمثّل التعليمة if ترويسة البنية، بينما يمثّل الاستدعائين المتداخلين للدالة ()print ضمن if متن البنية. يستخدم توثيق بايثون الرسمي مصطلح الكتلة للإشارة إلى جزء من شيفرات بايثون يُنفّذ مثل وحدة واحدة كما في حالات التعريف عن وحدة أو دالة أو صنف.

المتغير Variable والسمة Attribute

المتغيرات ببساطة هي أسماء تشير إلى الكائنات، أما السمات وفقًا لتوثيق بايثون الرسمي فهي أي اسم يتبع رمز النقطة، وترتبط السمات بالكائنات (وهي الأسماء التي تسبق النقطة). لنكتب على سبيل المثال الشيفرات التالية في الصدفة التفاعلية:

>>> import datetime
>>> spam = datetime.datetime.now()
>>> spam.year
2018
>>> spam.month
1

يمثل spam في الشيفرة السابقة متغيرًا يتضمّن كائنًا من نوع datetime (والمُعاد من التعليمة ()datetime.datetime.now)، في حين أن كل من year و month هي سمات لهذا الكائن. حتى لو كتبنا على سبيل المثال التعليمة ()sys.exit، ففي هذه الحالة تمثّل الدالة ()exit سمةً لكائن الوحدة sys.

تسمي بعض لغات البرمجة الأخرى السمات بالخاصيات properties أو صفات المتغيرات member variables.

الدوال Function والتوابع Method

الدالة هي مجموعة من الشيفرات التي تُشغَّل عند استدعائها؛ أما التابع فهو دالة (أو شيء قابل للاستدعاء callable، الأمر الذي سنشرحه في الفقرة التالية) مرتبطة بصنف ما، تمامًا مثل مبدأ كون السمات متغيرات مرتبطة بكائنات. تشمل الدوال تلك الموجودة أصلًا في المكتبة المبنية مسبقًا للغة built-in functions وتلك المخصصة للوحدات. لنكتب على سبيل المثال ما يلي في الصدفة التفاعلية:

>>> len('Hello')
5
>>> 'Hello'.upper()
'HELLO'
>>> import math
>>> math.sqrt(25)
5.0

في الشيفرة أعلاه، ()len هي دالة، بينما ()upper هو تابع للسلاسل النصية. تُعد التوابع مثل سمات للكائنات المرتبطة بها. وتجدر الملاحظة أن وجود النقطة لا يعني بالضرورة أن ما يليها تابعًا وليس دالة، ففي مثالنا السابق الدالة ()sqrt مرتبطة بالوحدة math، إذ أن math ليست صنفًا.

القابل للتكرار Iterable والمكرر Iterator

حلقة for التكرارية في بايثون متعددة الاستعمالات، إذ ستشغّل التعليمة (for i in range(3 كتلة من الشيفرة لثلاث مرات، فالاستدعاء (3)range ليس مجرد طريقة في بايثون لإعلام حلقة for برغبتنا بتكرار جزء من الشيفرة لثلاث مرات، لأن استدعائها يعيد كائن نطاق، تمامًا كما يعيد الاستدعاء ('list('cat كائن قائمة. كل من الكائنين السابقين أمثلة عن الكائنات القابلة للتكرار.

نستخدم الكائنات القابلة للتكرار في حلقات for التكرارية. لنكتب الشيفرات التالية في الصدفة التفاعلية بغية مشاهدة كيفية مرور الحلقة for على كائن نطاق وكائن قائمة:

>>> for i in range(3):
...   print(i) # body of the for loop
...
0
1
2
>>> for i in ['c', 'a', 't']:
...   print(i) # body of the for loop
...
c
a
t

كما تتضمن الكائنات القابلة للتكرار كافة الأنماط المتسلسلة من قبيل كائنات النطاق والقائمة والصف والسلاسل النصية، وبعضًا من كائنات الحاويات مثل كائنات القاموس والمجموعة والملف.

يحدث الكثير في كواليس أمثلة حلقات for هذه، إذ يستدعي بايثون كلًا من الدالتين ()iter و ()next المبنية مسبقًا لعمل حلقة for؛ فلدى استخدام حلقة for تُمرر الكائنات المُكرَّرة إلى الدالة المبنية مسبقًا ()iter، والتي تعيد كائناتٍ مكرِّرة. رغم كون الكائنات المكرَّرة تتضمن العناصر، إلا أن الكائن المكرِّر هو الذي يتتبع الكائن الواجب استخدامه في الخطوة التالية من الحلقة، فمن أجل كل تكرار للحلقة، يُمرَّر الكائن المكرِّر إلى الدالة ()next المبنية مسبقًا، لتعيد العنصر التالي من العناصر المكرَّرة. يمكن استخدام الدالتين ()iter و ()next يدويًا لمشاهدة كيفية عمل حلقات for التكرارية مباشرةً. لنكتب الشيفرات التالية في الصدفة التفاعلية لإنجاز نفس الأوامر كما في المثال السابق:

>>> iterableObj = range(3)
>>> iterableObj
range(0, 3)
>>> iteratorObj = iter(iterableObj)
>>> i = next(iteratorObj)
>>> print(i) # body of the for loop
0
>>> i = next(iteratorObj)
>>> print(i) # body of the for loop
1
>>> i = next(iteratorObj)
>>> print(i) # body of the for loop
2
>>> i = next(iteratorObj)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
1 StopIteration

لو استدعينا الدالة ()next بعد العنصر الأخير من الكائن القابل للتكرار المعاد، سيعرض بايثون استثناء وقف التكرار StopIteration المشار إليه بالرقم 1؛ بينما في حلقات for التكرارية وبدلًا من جعل البرنامج يتوقف، تستخدم هذا الاستثناء لمعرفة متى يجب وقف التكرار.

ويمكن للمكرِّر أن يمر على العناصر في الكائن القابل للتكرار مرةً واحدةً فقط، بما يشبه إمكانية استخدام الدالتين ()open و ()readlines لقراءة محتويات ملف ما لمرة واحدة فقط قبل أن تضطر إلى إعادة فتح الملف لقراءة محتوياته مجددًا، وفي حال رغبتك بالمرور على الكائن القابل للتكرار مجددًا، فلا بد من استدعاء الدالة ()iter مجددًا لتُنشئ كائنًا مكرِّرًا آخر. يمكنك إنشاء القدر الذي تريد من الكائنات المكرِّرة، إذ سيتتبع كل منها على حدى العنصر التالي الواجب إعادته. لنكتب ما يلي في الصدفة التفاعلية للاطلاع على كيفية العمل:

>>> iterableObj = list('cat')
>>> iterableObj
['c', 'a', 't']
>>> iteratorObj1 = iter(iterableObj)
>>> iteratorObj2 = iter(iterableObj)
>>> next(iteratorObj1)
'c'
>>> next(iteratorObj1)
'a'
>>> next(iteratorObj2)
'c'

لا تنسَ أن الكائنات المكرَّرة تُمرَّر مثل وسيط إلى الدالة ()iter، إذ أن الكائن المُعاد عن استدعاءات الدالة ()iter هو كائن مكرِّر. لتمرر الكائنات المكرِّرة إلى الدالة ()next. وفي حال إنشاء أنماط بيانات خاصة باستخدام تعليمات الصنف class، فعندها من الممكن تطبيق التوابع الخاصة ()__iter__ و ()__next__ للتمكن من استخدام الكائنات الخاصة في حلقات for.

أخطاء الصيغة وأخطاء زمن التنفيذ والأخطاء الدلالية

توجد العديد من طرق تصنيف الأخطاء، إلا أن التصنيف عالي المستوى للأخطاء البرمجية يقسمها إلى ثلاثة أنواع: أخطاء الصياغة Syntax وأخطاء زمن التنفيذ Runtime والأخطاء الدلالية Semantic.

يُقصد بالصياغة مجموعة القواعد اللازمة لكتابة أوامر صحيحة في لغة برمجة ما؛ إذ يتسبب خطأ الصياغة مثل إغفال أحد الأقواس أو استخدام نقطة بدلًا من الفاصلة أو غيرها من الأخطاء الإملائية بظهور خطأ صياغة SyntaxError في بايثون فورًا. تُعرف أخطاء الصياغة أيضًا باسم أخطاء التحليل parsing errors، والتي تحدث نتيجة عجز مفسر بايثون عن تحليل النص في الشيفرة المركزية إلى أوامر صحيحة. ولو شبهنا الأمر للغة العربية، فهذا الخطأ يكافئ حالات الأخطاء القواعدية أو حالة جملة من كلمات غير مترابطة لا معنى لها، فالحواسيب تتطلب وجود أوامر محددة ولا يمكنها قراءة أفكار المبرمج لمعرفة ما المتوقع من البرنامج فعله، وبالتالي لن يُشغّل البرنامج بوجود أخطاء صياغة.

يحدث خطأ زمن التنفيذ عندما يعجز برنامج ما قيد التشغيل عن أداء مهمة ما، كأن يحاول فتح ملف غير موجود أصلًا، أو أن يقسّم عددًا على الصفر. لو شبهنا الأمر للغة العربية، فهذا الخطأ يكافئ إعطاء أمر مستحيل مثل "ارسم مربعًا باستخدام ثلاثة أضلاع"، فإذا لم يجري التعامل مع خطأ زمن التنفيذ، سيتوقف البرنامج عارضًا رسالة تتبع للخطأ. يمكن اكتشاف أخطاء زمن التنفيذ باستخدام تعليمات try-except التي تشغّل الشيفرة المسؤولة عن معالجة الخطأ. على سبيل المثال، لنكتب الشيفرات التالية في الصدفة التفاعلية:

>>> slices = 8
>>> eaters = 0
>>> print('Each person eats', slices / eaters, 'slices.')

ستعرض الشيفرة السابقة تتبع الخطأ التالي عند تشغيلها:

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    print('Each person eats', slices / eaters, 'slices.')
ZeroDivisionError: division by zero

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

يكتشف مفسر بايثون أخطاء الصياغة في الشيفرة المصدرية قبل تشغيل البرنامج حتى، إلا أنها واردة الحدوث خلال التنفيذ، إذ يمكن على سبيل المثال، أن نمرر للدالة ()eval سلسلةً نصيةً مؤلفةً من شيفرات بايثون لتشغلها عند استدعائها، والتي قد تسبب خطأ في الصياغة أثناء التنفيذ. فمثلاً التعليمة ('(eval('print("Hello, world ينقصها علامة الاقتباس المزدوجة النهائية، الأمر الذي لن يكتشفه المفسر إلا عند استدعاء الدالة ()eval.

الأخطاء الدلالية (والتي تدعى أيضاَ بالأخطاء المنطقية) فهي أخطاء أدق، فهي لن تتسبب بظهور رسائل أخطاء أو حتى توقف تنفيذ البرنامج، لكنها تجعل الحاسوب ينفذ الأوامر بطريقة مختلفة عن تلك المقصودة من المبرمج، ولو شبهنا الأمر للغة العربية، فهذا الخطأ يكافئ إعلام الحاسوب بشراء علبة حليب، وفي حال وجود بيض في المتجر، أن يشتري دزينة (اثنتا عشر علبة). سيشتري الحاسب في هذه الحالة ثلاثة عشر علبة حليب لأن المتجر لديه بيض فعلًا؛ فالحواسيب تنفذ ما نخبرها به حرفيًا شئنا أم أبينا. لنكتب المثال التالي في الصدفة التفاعلية:

>>> print('The sum of 4 and 2 is', '4' + '2')

فنحصل على الخرج التالي:

The sum of 4 and 2 is 42

من الواضح أن القيمة 42 ليست الجواب الصحيح لعملية جمع العددين 4 و 2، ومع ذلك لم يتوقف البرنامج، إذ يجمع عامل الجمع + في بايثون الأعداد الصحيحة في حين أنه يربط قيم السلاسل النصية في سلسلة واحدة جديدة، وبالتالي حصلنا على جواب خاطئ بسبب استخدام كل من 4 و 2 مثل سلاسل نصية وليس أعداد صحيحة ما سبب الحصول على هذه النتيجة غير المرغوبة.

المعاملات Parameters والوسطاء Arguments

تُعرّف المعاملات بأنها أسماء المتغيرات الموجودة بين قوسين ضمن تعليمة التصريح عن الدوال def، أما الوسطاء فهي القيم الممررة لدى استدعاء الدوال، والتي تُسند لاحقًا إلى المعاملات. فعلى سبيل المثال، لنكتب ما يلي في الصدفة التفاعلية:

1 >>> def greeting(name, species):
...     print(name + ' is a ' + species)
...
2 >>> greeting('Zophie', 'cat')
Zophie is a cat

كل من name و species في تعليمة def السابقة في السطر رقم 1 هي معاملات، أما 'Zophie' و 'cat' في استدعاء الدالة في السطر رقم 2 فهي وسطاء. يحدث خلط بين هذين المصطلحين عادةً، ولعل من الجيد تذكر أن المعاملات والوسطاء ما هي سوى أسماء أخرى للمتغيرات والقيم على التوالي عند استخدامها في هذا السياق آنف الذكر.

تحويل نوع البيانات الضمني Type Coercion والصريح Type Casting

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

تجري بايثون غالبًا تحويلًا ضمنيًا للنوع، كما في حالة تقييم التعبير البرمجي 3.0+2 إلى القيمة 5.0، إذ تُجبر القيمتان 2 و 5.0 على نوع شائع من أنواع البيانات والذي يتمكن المفسر من التعامل معه، ويُدعى هذا التحويل بالتحويل الضمني.

قد يؤدي التحويل الضمني أحيانًا إلى نتائج غير متوقعة، إذ يمكن مثلًا أن تُحوّل القيم المنطقية True و False ضمنيًا إلى القيم العددية الصحيحة 1 و 0 على التوالي، رغم أنه من المستحيل أن نعبّر عن القيم المنطقية في الشيفرات الواقعية مستخدمين تلك القيم العددية؛ ما يعني أن التعبير True + False + True يكافئ التعبير 1 + 0 + 1 وسيُقيّم إلى 2. بعد معرفتك لهذا الأمر قد تعتقد أن تمرير قائمة من القيم المنطقية إلى التابع ()sum يمثّل طريقةً جيدةً في معرفة عدد القيم True في تلك القائمة، إلا أن استخدام التابع ()count لهذا الغرض أسرع.

الخاصيات Properties والسمات Attributes

يُستخدم مصطلحي الخاصيات والسمات في العديد من لغات البرمجة على أنهما كلمتان مترادفتان، لكن لهذين المصطلحين معانٍ مختلفة عن بعضهما في بايثون. فالسمة هي اسم مرتبط بكائن، وتتضمن المتغيرات والتوابع المرتبطة بالكائن.

تمتلك لغات البرمجة الأخرى مثل جافا تمتلك توابع getter (الذي تعيد القيمة الموافقة لاسم متغير) و setter (الذي تأخذ معاملًا لتُسنده إلى اسم المتغير) للأصناف، فبدلًا من القدرة على إسناد قيمة إلى السمة مباشرةً، يجب على المبرمج استدعاء التابع setter لهذه السمة، بحيث تضمن الشيفرة الموجودة ضمن التابع setter أن قيمةً مناسبةً للمتغير الخاص بالكائن قد أُسندت إليه؛ بينما يقرأ التابع getter قيمة السمة. لو كانت السمة باسم accountBalance على سبيل المثال، فسيُسمى كلًا من التابعين getter و setter بالشكل ()setAccountBalance و ()getAccountBalance على التوالي.

أما في بايثون، فتتيح الخاصيات للبرامج استخدام كل من getters و setters بصياغة أوضح.

الشيفرة الثنائية Bytecode والشيفرة التنفيذية للتعليمات Machine Code

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

تمتلك لغة البرمجة سي C برنامج مترجم قادر على ترجمة شيفرات سي المصدرية إلى ثنائي لمعظم وحدات المعالجة المركزية المتوفرة، أما في حال تشغيل لغة برمجة مثل بايثون على نفس مجموعة وحدات المعالجة المركزية، فلا بُد من إنجاز كم هائل من العمل لكتابة مترجمات بايثون مخصصة لكل منها.

توجد طريقة أخرى لتحويل الشيفرات المصدرية إلى شيفرات تنفيذية للتعليمات (قابلة للتنفيذ من قبل الحاسب)؛ فبدلًا من إنشاء شيفرة تنفيذية للتعليمات لتُنفّذ مباشرةً من قبل وحدة المعالجة المركزية، يمكن إنشاء شيفرة ثنائية والتي تسمى أيضًا بالشيفرة المحمولة أو p-code، والتي تُنفّذ من قبل مفسر بدلًا من وحدة المعالجة المركزية مباشرةً. تتكون هذه الشيفرة من تعليمات مختارة من مجموعة تعليمات، ورغم كون هذه التعليمات غير قابلة للتنفيذ من قبل وحدات المعالجة المركزية، إلا أن المفسر ينفذ هذه التعليمات.

تُخزّن شيفرات بايثون الثنائية ضمن ملفات بالامتداد "pyc."، وقد تلاحظها جنبًا إلى جنب مع ملفاتك ذات الامتداد "py." المتضمنة للشيفرات المصدرية. المفسر CPython المكتوب بلغة سي قادر على ترجمة شيفرة بايثون المصدرية إلى شيفرة بايثون ثنائية لينفّذ لاحقًا التعليمات. ينطبق الأمر ذاته على برنامج Java Virtual Machine -أو اختصارًا JVM- في لغة جافا، والذي ينفذ شيفرات جافا الثنائية. بما أن المفسر CPython مكتوب بلغة سي، فإنه يمتلك مفسر بايثون، وهو قادر على ترجمة الشيفرات لتناسب أي وحدة معالجة مركزية ذات مفسر خاص بها بلغة سي.

السكريبت Script والبرنامج Program، لغات كتابة السكريبت ولغات البرمجة

الفرق بين السكريبت والبرنامج أو حتى ما بين لغات كتابة السكريبت ولغات البرمجة غامض، فمن المنطقي القول بأن كل سكريبت هو برنامج، وأن كل لغات كتابة السكريبت هي لغات برمجة، لكن تُعد لغات كتابة السكريبت أسهل أحيانًا، أو أنها ليست لغات برمجة حقيقية.

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

يمكن في الواقع مناقشة الاختلافات، إلا أنها ليست بتلك الأهمية، فلغات كتابة السكريبت ليست بالضرورة أقل قدرة ولا لغات البرمجة بالضرورة أصعب للتعامل معها.

المكتبة Library وإطار العمل Framework وحزمة أدوات تطوير البرمجيات SDK والمحرك Engine وواجهة برمجة التطبيقات API

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

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

إطار العمل هو مجموعة من الشيفرات عكسية التحكم inversion of control، بمعنى أن المطور يُنشئ دوالًا ليستدعيها إطار العمل عند الحاجة، على عكس استدعاء الدوال في شيفرات المطور. يُشبّه عادة مصطلح اللاتحكمية بعبارة "لا تتصل بنا، سنتصل نحن بك". على سبيل المثال، تتضمن كتابة شيفرة لإطار عمل تطبيق ويب إنشاء دوال لصفحات الويب التي سيستدعيها إطار العمل لدى وصول طلب ويب.

تتضمن حزمة أدوات التطوير software development kit- أو اختصارًا SDK- مكتبات شيفرات وتوثيقات وأدوات برمجية للمساعدة في في إنشاء تطبيقات عاملة على أنظمة تشغيل أو منصات معينة. على سبيل المثال، تُستخدم كل من Android SDK و iOS SDK لإنشاء تطبيقات الهاتف المحمول لأنظمة أندرويد و iOS على التوالي، كما أن حزمة تطوير جافا Java Development Kit-أو اختصارًا JDK- ما هي إلا SDK مُعدّة لإنشاء التطبيقات العاملة على JVM.

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

واجهة برمجة التطبيقات Application Programming Interface -أو اختصارًا API- هي الواجهة العمومية للمكتبة، أو حزمة أدوات التطوير، أو إطار العمل، أو المحرك؛ إذ أنها تحدد كيفية استدعاء الدوال أو إنشاء طلبات الوصول إلى موارد المكتبة. يقع على عاتق مُنشئي المكتبات إعداد التوثيقات حول واجهة برمجة التطبيقات المتوفرة، وتنشئ العديد من الشبكات الاجتماعية الشائعة والمواقع الإلكترونية واجهة برمجة التطبيقات من نوع HTTP API، التي تتيح للبرامج الوصول إلى خدماتها ذاتيًا، إذ يتيح لك استخدام واجهات برمجة التطبيقات هذه كتابة برامج قادرة مثلًا على النشر ذاتيًا على فيسبوك أو قراءة الأخبار عبر تويتر.

الخلاصة

من السهل أن تمارس البرمجة لسنوات مع بقاء بعض المصطلحات البرمجية تثير الالتباس بالنسبة لك، وبما أن معظم التطبيقات البرمجية تُنشئ من قبل فريق كامل من المطورين وليس من قبل أفراد، فلا بُد من فهم المصطلحات بدقة والتمييز بين المتقارب منها.

لبعض المصطلحات مثل القيم والمتغيرات والدوال أسماء مختلفة باختلاف السياق، مثل العناصر والمعاملات والوسطاء والتوابع، ومن السهل الخلط بين العديد من المصطلحات، إذ لا يعد الخلط بين بعض المصطلحات في الحياة اليومية أمرًا كارثيًا، مثل الخلط بين الخاصية والسمة أو الكتلة والمتن أو الاستثناء والخطأ، أو حتى الاختلافات الدقيقة ما بين المكتبة وإطار العمل وحزمة أدوات التطوير والمحرك وواجهة برمجة التطبيقات، فلا يؤدي فهمها الخاطئ بالضرورة إلى عدم عمل شيفرتك، إلا أنك ستظهر بمظهر غير المحترف، فعلى سبيل المثال، يخلط المبتدئون ما بين مفاهيم من قبيل التعابير والتعليمات البرمجية أو الدوال والتوابع أو المعاملات والوسطاء، إلا أن مصطلحاتٍ أخرى مثل قابل للتكرار والمكرِّر أو خطأ الصياغة والخطأ الدلالي أو الشيفرة الثنائية والشيفرة التنفيذية للتعليمات، فلكل منها معناها الخاص الذي عليك فهمه بعمق وعدم الخلط بينه وبين مقابله مالم ترغب بإثارة الاستغراب لدى زملاء عملك.

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

ترجمة -وبتصرف- لجزء من الفصل السابع "المصطلحات البرمجية" من كتاب Beyond the Basic Stuff with Python لصاحبه Al Sweigart.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...