البيانات وأنواعها وهياكل البيانات

البيانات وأنواعها وهياكل البيانات

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

أبسط وصف لبرنامج الكمبيوتر هو التالي:

يقوم برنامج الكمبيوتر بتخزين البيانات، ويقوم بأشياء معيّنة بهذه البيانات.

وصف برنامج الكمبيوتر

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

لا يمكن أن يوجد برنامج كمبيوتر بدون بيانات. يمكن أن تكون البيانات اسمًا أو رقمًا بسيطًا كما هو الحال في لعبة Angry Goblin، أو يمكن أن يكون لديك مجموعة بيانات معقدة للغاية وذات تنظيم وهيكلية متقدمة. وإذا كنت تستخدم لغة برمجة، فستحتاج إلى تنفيذ بعض الإجراءات باستخدام البيانات وتحويلها بطريقة أو بأخرى.

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

سيغطي الجزء الأول من هذا الفصل بعض النظريات المتعلقة بأنواع البيانات. في الجزء الثاني، ستعمل على مشروع جديد حيث ستعمل على حل جميع الكلمات التي استخدمتها الكاتبة “جين أوستن” عند كتابة مؤلف: “كبرياء وتحامل” Pride and Prejudice وعدد المرات التي استخدمت فيها كل كلمة. هل يمكنك تخمين الكلمات الخمس الأكثر شيوعًا في ذلك الكتاب؟ ستجد الإجابة عند إكمال المشروع لاحقًا في هذا الفصل.

فئات أنواع البيانات

لقد صادفت بالفعل العديد من أهم أنواع البيانات في Python. لقد استخدمت الأعداد الصحيحة integers والأعداد العائمة أو العشرية floats، والسلاسل strings، والقيم المنطقية Booleans، والقوائم lists. هناك العديد من أنواع البيانات في بايثون. الكثير والكثير!

عندما تتعرّف على المزيد من أنواع البيانات، من المفيد التعرّف على فئات أنواع البيانات Categories of Data Types. هذا لأن بعض أنواع البيانات قد يكون لها خصائص متشابهة، وعندما تعرف كيفية التعامل مع فئة من نوع البيانات، ستكون أفضل تجهيزًا للتعامل مع نوع البيانات الجديد الذي ستصادفه من نفس الفئة.

في هذا القسم، سوف تتعرّف على أنواع البيانات المتسلسلة sequences والقابلة للتكرار iterables والمتغيرة mutable  وغير القابلة للتغيير immutable . لا تنزعج من الصياغة الغامضة. مثل أي مهنة أخرى، يحب المبرمجون استخدام كلمات معقّدة لجعل الموضوع يبدو صعبًا ومُبهمًا. إن مصطلح “لا يمكن تغييره” Cannot-be-changed لا يبدو كبيرًا كما هو مصطلح “غير قابل للتغيير” immutable! هدفي في هذا الكتاب هو عكس ذلك، لكن لا يمكننا الهروب من استخدام المصطلحات التي ستجدها في التوثيق ورسائل الخطأ والنصوص الأخرى.

أنواع البيانات القابلة للتكرار

لقد سبق لك استخدام التكرار في بايثون. عندما تكرر كتلة من التعليمات البرمجية عدة مرات باستخدام حلقة for، فأنت تقوم بتكرار كتلة التعليمات البرمجية. ستسمع غالبًا عن التكرار الأول أو التكرار الثاني للحلقة، على سبيل المثال، للإشارة إلى إحدى التكرارات.

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

a_number = 5
another_number = 5.3
my_name = "Stephen"
many_numbers = [2, 5, 7, 23, 1, 4, 10]
more_numbers = range(10)

تذكّر أنه يمكنك دائمًا التحقّق من نوع البيانات باستخدام دالة أو وظيفة type ():

print(type(a_number))
print(type(another_number))
print(type(my_name))
print(type(many_numbers))
print(type(more_numbers))

الإخراج الذي ستحصل عليه من طباعة أنواع البيانات لهذه المتغيرات الخمسة هو:

<class 'int'>
<class 'float'>
<class 'str'>
<class 'list'>
<class 'range'>

أنت تعرف بالفعل الأربعة الأولى من هؤلاء. ستتذكر أنك استخدمت الدالة range () عندما تعلّمت لأول مرة عن حلقة for. لتكرار بعض الكود عشر مرات، على سبيل المثال، كتبت ما يلي:

for item in range(10):
    print(item)

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

الكود التالي مطابق لحلقة for التي كتبتها أعلاه، حيث أن more_numbers يحتوي على البيانات التي تم إرجاعها من الدالة range(10):

for item in more_numbers:  # Same as for item in range(10):
    print(item)

يمكنك استكشاف أنواع البيانات القابلة للتكرار من خلال محاولة استخدام كل متغير في حلقة for:

for item in a_number:  # a_number is an int
    print(item)

المتغير a_number هو عدد صحيح int، وعندما تحاول استخدامه في حلقة for، ستصلك رسالة الخطأ التالية:

Traceback (most recent call last):
  File "<path>/<filename>.py", line 13, in <module>
    for item in a_number:
TypeError: 'int' object is not iterable

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

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

for item in my_name:  # my_name is a str
    print(item)

for item in many_numbers:  # many_numbers is a list
    print(item)

لن تحصل على أخطاء تفيد بأن أنواع البيانات هذه غير قابلة للتكرار في هذه الحالة، بل ستحصل على:

S
t
e
p
h
e
n
2
5
7
23
1
4
10

عندما تقوم بالتكرار خلال سلسلة، يتم اعتبار كل حرف واحدًا في كل مرة. لذا فإن المتغير item سيحتوي على الحرف “S” في التكرار الأول للحلقة for، ثم حرف t في التكرار الثاني، وهكذا.

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

في جملة الحلقة for، لا تحتاج بالضرورة إلى استخدام اسم متغير في نهاية السطر. لقد رأيت بالفعل كيف يمكنك استخدام دالة مثل دالة النطاق range(). طالما أن الدالة تُرجع نوع بيانات قابل للتكرار، فيمكن استخدامها مباشرة في جملة حلقة for. يمكنك حتى استخدام بنية البيانات data structure مباشرة، على سبيل المثال:

for item in "Stephen":
    print(item)

for item in [2, 5, 7, 23, 1, 4, 10]:
    print(item)

فئة أخرى من أنواع البيانات هي التسلسل Sequence. التسلسل هو نوع بيانات يحتوي على عناصر مرتّبة داخله. لقد رأيت بالفعل على سبيل المثال كيف يمكننا استخدام الفهرسة indexing في كل من القوائم lists والسلاسل strings – كلاهما من التسلسلات – لاستخراج عنصر من موضع معين، على سبيل المثال:

>>> my_name = "Stephen"
>>> my_name[2]
'e'

>>> many_numbers = [2, 5, 7, 23, 1, 4, 10]
>>> many_numbers[0]
2

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

أنواع البيانات القابلة للتغيير وغير القابلة للتغيير

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

يمكنك الآن محاولة إعادة تعيين قيمة جديدة لموضع معين. لنبدأ بالقوائم:

>>> many_numbers = [2, 5, 7, 23, 1, 4, 10]
>>> many_numbers[2] = 2000
>>> many_numbers
[2, 5, 2000, 23, 1, 4, 10]

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

يمكنك الآن محاولة فعل الشيء نفسه بسلسلة:

>>> my_name = "Stephen"
>>> my_name[2] = "y"
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'str' object does not support item assignment

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

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

لاحظ أنه حتى مع أنواع البيانات غير القابلة للتغيير، لا يزال يُسمح لك بالكتابة فوق المتغير بأكمله. لذلك إذا أردت تغيير الحرف الثالث من اسمي إلى “y”، فسأحتاج إلى القيام بما يلي:

>>> my_name = "Stephen"
>>> my_name = "Styphen"
>>> my_name
'Styphen'

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

الطرق المرتبطة بأنواع البيانات

هناك الكثير من المصطلحات في البرمجة. إليك مصطلح جديد لم تصادفه حتى الآن: طريقة Method.

إذا كنت تعرف ما هي الدالة، وقمت بذلك من الفصل السابق، فأنت تعرف أيضًا ما هي الطريقة Method. الطريقة هي وظيفة أو دالة مرتبطة بنوع بيانات معيّن وتعمل على نوع البيانات هذا. سيكون هذا الوصف أكثر منطقية مع بعض الأمثلة.

لنبدأ بالدوال القياسية. لقد رأيت دالة print () ودالة range (). وهي مكتوبة بأحرف صغيرة ولها أقواس في نهايتها. يمكن أن تحتوي الدوال على وسيطات بين قوسين، وتقوم بإرجاع البيانات عندما تُكمل إجراءاتها.

لقد رأيت بالفعل طريقة واحدة مستخدمة في الفصل السابق. عندما أنشأت قائمة ثم أردت إضافة عناصر إلى القائمة التي استخدمتها append (). لنتذكر كيف يمكنك استخدام append ():

>>> many_numbers = [2, 5, 7, 23, 1, 4, 10]
>>> many_numbers.append(5000)
>>> many_numbers
[2, 5, 7, 23, 1, 4, 10, 5000]

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

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

إذا كنت تستخدم بيئة تطوير IDE مثل بيئة PyCharm، فستلاحظ الآن أن هذه الأدوات بها الإكمال التلقائي لتسهيل كتابة التعليمات البرمجية الخاصة بك. إذا بدأت في كتابة الأحرف pri، فإن IDE سيعرض لك print ()، ويمكنك ببساطة الضغط على Enter / Return لقبول الإكمال التلقائي. ربما لاحظت أنه عند كتابة اسم متغير متبوعًا بنقطة، سيعرض IDE الخاص بك قائمة بالأسماء المتاحة لك لاستخدامها. ستجد جميع الطرق التي يمكنك استخدامها مدرجة هنا.

طرق القوائم List Methods

نظرًا لأن many_numbers عبارة عن قائمة، فعند كتابة النقطة، سترى جميع الطرق المرتبطة بالقوائم. متى كانت لديك قائمة، ستتمكّن دائمًا من الوصول إلى طرق القائمة list methods.

دعونا نلقي نظرة على بعض طرق القائمة الأخرى:

>>> many_numbers.remove(23)
>>> many_numbers
[2, 5, 7, 1, 4, 10, 5000]

تحتوي القائمة many_numbers على القيمة 23 المخزنة في الموضع الرابع. طريقة الإزالة remove() تحذف هذه القيمة. إذا كنت تريد حذف قيمة حسب الموضع داخل القائمة، بدلاً من حذفها حسب القيمة، يمكنك استخدام طريقة الإزالة pop ():

>>> many_numbers.pop(0)
2
>>> many_numbers
[5, 7, 1, 4, 10, 5000]

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

>>> the_number = many_numbers.pop(0)
>>> the_number
5
>>> many_numbers
[7, 1, 4, 10, 5000]

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

>>> many_numbers.append(4)
>>> many_numbers
[7, 1, 4, 10, 5000, 4]
>>> many_numbers.count(4)
2

تحسب طريقة العدّ count () عدد المرات التي تظهر فيها القيمة الموجودة بين الأقواس في القائمة. نظرًا لوجود تواجدين للقيمة 4، تُرجع هذه الطريقة القيمة 2.

هذه الطرق هي طرق القائمة. تم تعريفهم للعمل على القوائم.

طرق السلاسل String Methods

دعنا نلقي نظرة على بعض طرق السلاسل string methods الآن:

>>> my_name = "Stephen"
>>> my_name.upper()
'STEPHEN'
>>> my_name.lower()
'stephen'
>>> my_name.replace("e", "-")
'St-ph-n'

كما في السابق مع القوائم، إذا كنت تستخدم IDE وقمت بكتابة اسم سلسلة متبوعة بنقطة، فسترى جميع الطرق المرتبطة بالسلاسل.

تحليل سلوك البيانات المتغيرة والثابتة

دعنا نعود إلى الأمثلة الأخيرة التي كنت تجربها:

>>> my_name = "Stephen"
>>> my_name.upper()
'STEPHEN'
>>> my_name
'Stephen'

لم يتغير المتغير my_name عند استخدام طريقة الأحرف الكبيرة upper(). يمكنك تجربة ذلك باستخدام طرق السلسلة الأخرى التي استخدمتها أعلاه، وستلاحظ نفس النمط.

طريقة upper()، وطرق السلسلة الأخرى، تعيد نسخة من السلسلة لكنها لا تغيّر المتغيّر الأصلي. ومع ذلك، لم يكن هذا هو الحال مع طرق القائمة. لنلقِ نظرة على أحد الأمثلة التي استخدمتها أعلاه:

>>> many_numbers = [2, 5, 7, 23, 1, 4, 10]
>>> many_numbers.append(5000)
>>> many_numbers
[2, 5, 7, 23, 1, 4, 10, 5000]

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

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

إذا أردت استبدال السلسلة القديمة بالجديدة التي تعرضها الطريقة، فسيتعين عليك القيام بذلك صراحة:

>>> my_name = "Stephen"
>>> my_name = my_name.upper()
>>> my_name
'STEPHEN'

يمكنك تعيين نسخة من السلسلة التي تم إرجاعها بواسطة upper () إلى نفس اسم المتغير my_name. لذلك فأنت تقوم بالكتابة فوق المتغير الأصلي. هذه الخطوة الإضافية تجبرك، أنت المبرمج، على تحمّل مسؤولية هذا الإجراء. أنت تؤكد أنك تريد استبدال السلسلة الأصلية بالسلسلة الجديدة.

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

هياكل البيانات

لقد واجهت عدة أنواع من البيانات بالفعل. بعض أنواع البيانات، تسمى هياكل البيانات Data Structures، تخزّن مجموعة من القيم. لغة Python لديها ثلاثة هياكل أساسية للبيانات. لقد استخدمت بالفعل واحدة من هؤلاء، وهي القائمة List. القائمة عبارة عن تسلسل وقابل للتكرار، ويمكن تغييره. ستتعرّف قريبًا على بنيتي البيانات الأساسيتين الأخريين في بايثون: المجموعات Tuples والقواميس Dictionaries.

يوجد في Python العديد من أنواع البيانات الأخرى، بما في ذلك هياكل البيانات. في الجزء الثاني من هذا الكتاب، ستتعرّف على المزيد من هياكل البيانات المتقدمة المستخدمة للتعامل مع مجموعات البيانات الكميّة في العلوم والمالية والمجالات الأخرى التي تعتمد على البيانات.

المجموعات Tuples

ثاني هياكل البيانات الأساسية في Python هي المجموعة tuple. لديك خيار في كيفية نطق هذا المصطلح! ينطقه البعض على أنه “تابل” على وزن كلمة couple. يقولها الآخرون على أنها “تو بل” على وزن Tow pill. على الرغم من أن tuple ليست كلمة إنجليزية شائعة – فهي مصطلح يظهر في الرياضيات – إنه نفس الجذر الذي يظهر في نهاية الكلمات مثل ثلاثي triple أو رباعي quadruple أو متعدد multiple. وسترى الرابط بين tuple وهذه الكلمات قريبًا.

يمكنك إنشاء مجموعة بطريقة مماثلة كما تفعل مع القائمة. ومع ذلك، فإن نوع القوس المرتبط بالمجموعات هو الأقواس العادية بدلاً من الأقواس المربعة:

>>> some_numbers = (3, 5, 67, 12, 3, 5)
>>> type(some_numbers)
<class 'tuple'>

يمكنك إنشاء قائمة بالأرقام نفسها بحيث يمكنك استكشاف أوجه الاختلاف والتشابه بين القوائم والمجموعات:

>>> same_numbers_in_list = [3, 5, 67, 12, 3, 5]
>>> type(same_numbers_in_list)
<class 'list'>

المجموعات هي أيضًا تسلسلات ويمكنك استخدام الفهرسة indexing والتقسيم إلى شرائح slicing في المجموعات بالطريقة نفسها التي تستخدمها مع القوائم:

>>> some_numbers[1]
5
>>> some_numbers[2:4]
(67, 12)
>>> some_numbers[-1]
5

لاحظ أنه عند استخدام الفهرسة أو التقسيم إلى شرائح لأي تسلسل، ستستخدم دائمًا الأقواس المربعة بعد اسم المتغير مباشرةً، حتى بالنسبة للتسلسلات التي ليست قوائم.

يمكنك التحقق مما إذا كانت المجموعات قابلة للتكرار باستخدام tuple في حلقة for ومعرفة ما إذا كان سيظهر لديك خطأ:

>>> for item in some_numbers:  # some_numbers is a tuple
...     print(item)
...     
3
5
67
12
3
5

لقد تمكنت من التكرار من خلال المجموعة some_numbers بنجاح. هذا يعني أن المجموعات قابلة للتكرار.

يمكنك الآن التحقق من قابلية التغيير. يمكنك مقارنة إعادة تعيين القيمة لأحد العناصر في المجموعة بما يحدث في حالة استخدام القائمة:

>>> same_numbers_in_list[2] = 1000
>>> same_numbers_in_list
[3, 5, 1000, 12, 3, 5]

عند استخدام قائمة مثل same_numbers_in_list، يمكنك إعادة تعيين أحد عناصر القائمة. ومع ذلك، إذا حاولت أن تفعل الشيء نفسه مع tuple، فسوف تحصل على خطأ:

>>> some_numbers[2] = 1000
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

هذا هو نفس الخطأ الذي حصلت عليه عندما حاولت إعادة تعيين حرف جديد في سلسلة. إن المجموعات Tuples، مثل السلاسل Strings، غير قابلة للتغيير. إذن، المجموعة Tuple هي تسلسل Sequence غير قابل للتغيير Immutable.

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

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

بالرغم من أن الأقواس المستديرة، أو الأقواس العادية، هي نوع الأقواس المرتبطة بالمجموعات، إلا أنه يمكنك أيضًا حذف الأقواس تمامًا عند إنشاء المجموعات:

>>> some_numbers = 3, 5, 67, 12, 3, 5
>>> type(some_numbers)
<class 'tuple'>

كما هو الحال مع القوائم، يمكنك تخزين أي نوع بيانات في المجموعة، بما في ذلك هياكل البيانات الأخرى:

>>> another_tuple = (3, True, [4, 5], "hello", (0, 1, 2), 5.5)

تحتوي هذه المجموعة على ستة عناصر:

  • عدد صحيح Int
  • قيمة منطقية Boolean
  • قائمة list
  • سلسلة string
  • مجموعة tuple
  • عدد عائم float

في الوقت الحالي، لا داعي للقلق بشأن الحيرة بين استخدام قائمة أو مجموعة. ومع ذلك، ستصادف المجموعات tuples أثناء قيامك بالبرمجة حيث تستخدم Python بنية البيانات هذه في كثير من الأحيان.

القواميس Dictionaries

هيكل البيانات الأساسي الثالث هو القاموس dictionary. تخيل أنك بحاجة إلى تخزين علامات الاختبار للطلاب في الفصل. يمكنك القيام بذلك بقائمتين:

>>> student_names = ["John", "Kate", "Trevor", "Jane", "Mark", "Anne"]
>>> test_results = [67, 86, 92, 55, 59, 79]

طالما يتم تخزين الأسماء ونتائج الاختبار بنفس الترتيب، يمكنك عندئذٍ استخراج القيم في نفس المواضع في كل قائمة. على سبيل المثال، إذا قمت باستخراج العنصر الثاني من كل قائمة، فسيكون لديك اسم “Kate” ونتائج الاختبار الخاصة بها، 86.

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

في سيناريو تشبيه الغرفة البيضاء White Room، تقوم بإنشاء صندوقين منفصلين، أحدهما بعنوان student_names والآخر بعنوان test_results. على الرغم من أنك تدرك أن هذين الصندوقين لهما بيانات مرتبطة ببعضهما البعض، إلا أن برنامج الكمبيوتر لا يعرف أن هذه الصناديق مرتبطة. هناك طريقة أفضل لتخزين هذا النوع من المعلومات المرتبطة وهي: القواميس dictionaries.

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

>>> student_marks = {"John": 67, "Kate": 86, "Trevor": 92, "Jane": 55, "Mark": 59, "Anne": 79}
>>> type(student_marks)
<class 'dict'>

بالإضافة إلى استخدام الأقواس المتعرّجة، ربما لاحظت اختلافًا آخر عند إنشاء قاموس. يحتوي كل عنصر في القاموس على جزأين مفصولين بنقطتين :. يحتوي القاموس student_marks على ستة عناصر، وليس اثني عشر. العنصر الأول هو الزوج: “John” و67 وهكذا.

الجزء الأول من كل عنصر يسمى المفتاح Key. الجزء الثاني يسمى القيمة Value. إذن في العنصر الأول من القاموس أعلاه، المفتاح هو “John”، والقيمة هي 67.

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

>>> student_marks[2]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
KeyError: 2

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

>>> student_marks["Trevor"]
92

القواميس وقابلية التغيير

دعونا نرى ما إذا كانت القواميس قابلة للتغيير mutable:

>>> student_marks["Kate"] = 99
>>> student_marks
{'John': 67, 'Kate': 99, 'Trevor': 92, 'Jane': 55, 'Mark': 59, 'Anne': 79}

الجواب: “نعم”. القواميس هي نوع بيانات قابل للتغيير. لقد غيّرت القيمة المرتبطة بـ “Kate” إلى 99. يمكنك أيضًا زيادة القيمة في القاموس:

>>> student_marks["Anne"] = student_marks["Anne"] + 1
>>> student_marks
{'John': 67, 'Kate': 99, 'Trevor': 92, 'Jane': 55, 'Mark': 59, 'Anne': 80}

ستنظر Python فيما هو على يمين عامل الإسناد = حيث ستقرأ القيمة المرتبطة بالمفتاح “Anne”. سيقوم البرنامج بعد ذلك بإضافة 1 إلى العدد الصحيح 79. ثم يتم إعادة تعيين النتيجة إلى المفتاح “Anne”.

ماذا يحدث إذا حاولت الوصول إلى قيمة مفتاح key غير موجود؟

>>> student_marks["Matthew"]
Traceback (most recent call last):
  File "<input>", line 1, in <module>
KeyError: 'Matthew'

المفتاح “Matthew” غير موجود، لذلك تحصل على خطأ في المفتاح KeyError. ومع ذلك، يمكنك إنشاء مفتاح جديد إذا قمت بتعيين قيمة له:

>>> student_marks["Matthew"] = 50
>>> student_marks
{'John': 67, 'Kate': 99, 'Trevor': 92, 'Jane': 55, 'Mark': 59, 'Anne': 80, 'Matthew': 50}

يمكنك الآن إلقاء نظرة على بعض الطرق Methods التي يمكنك استخدامها مع القواميس. يمكنك البدء بطريقتين ليستا مثيرتين للغاية ولكنها قد تكون مفيدة للغاية:

>>> student_marks.keys()
dict_keys(['John', 'Kate', 'Trevor', 'Jane', 'Mark', 'Anne', 'Matthew'])

>>> student_marks.values()
dict_values([67, 99, 92, 55, 59, 80, 50])

طريقة أخرى مفيدة هي get () والتي تتيح لك الحصول على القيمة المرتبطة بمفتاح معيّن. يمكنك بالفعل أن تفعل ذلك باستخدام تدوين الأقواس المربعة، student_marks[“John”]، كما رأيت أعلاه. ومع ذلك، فإن طريقة get () لا تعطي خطأ إذا حاولت الوصول إلى قيمة مفتاح غير موجود:

>>> student_marks.get("Anne")
80
>>> student_marks.get("Zahra")

>>>

عند استخدام “Anne” كوسيطة لـ get ()، أعادت الطريقة القيمة 80. ومع ذلك، لا يتم إرجاع أي شيء عندما تكون الوسيطة مفتاحًا غير موجود. ومع ذلك، لا تحصل على خطأ KeyError أيضًا. هناك أوقات قد ترغب فيها أن تكون شفرتك قوية بحيث إذا حاولت الوصول إلى مفتاح غير موجود، يستمر تشغيل البرنامج دون حدوث خطأ. تتيح لك طريقة get () أيضًا وضع قيمة افتراضية بحيث يمكنك التحكم فيما ترجع إليه الطريقة إذا لم يكن المفتاح موجودًا:

>>> student_marks.get("Anne", "There is no student with this name")
80
>>> student_marks.get("Zahra", "There is no student with this name")
'There is no student with this name'

هنا حددت إخراج الرسالة: “لا يوجد طالب بهذا الاسم” عندما لا يكون المفتاح موجودًا.

القواميس وقابلية التكرار

ماذا عن حلقات for؟ هل يمكنك التكرار من خلال قاموس؟

>>> for stuff in student_marks:
...     print(stuff)
...     
John
Kate
Trevor
Jane
Mark
Anne
Matthew

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

مشروع تحليل تكرارات الكلمات

حان الوقت لبدء العمل في مشروع جديد لدمج الكثير مما تعلمته حتى الآن. مهمتك هي قراءة وتحليل رواية “كبرياء وتحامل” لجين أوستن. إلا أنك لن تقرأ الرواية! في هذا المشروع، ستكتشف كل الكلمات التي استخدمتها Jane Austen في الكتاب وعدد مرات استخدام كل كلمة.

ذكرت سابقًا أنك لن تقرأ الكتاب. ومع ذلك، فإن برنامج الكمبيوتر الخاص بك سوف يفعل. لذلك، قبل أن تبدأ العمل في مشروع Pride & Prejudice، ستعرف كيفية قراءة البيانات من مصدر خارجي.

ستحتاج إلى الملف المسمى pride_and_prejudice.txt لهذا المشروع والذي يمكنك الحصول عليه من مستودع ملفات كتاب البرمجة بلغة Python. ستحتاج إلى وضع الملف في مجلد المشروع.

  • تحميل مستودع ملفات كتاب البرمجة بلغة بايثون Python

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

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

  • جعل الملفات في متناول مشروعك

إن أبسط طريقة للتأكد من أنه يمكنك الوصول إلى ملف من مشروع بايثون Python الخاص بك هو وضعه في مجلد المشروع – نفس المجلد حيث توجد برامج Python النصيّة الخاصة بك. إذا كنت تستخدم بيئة تطوير متكاملة IDE مثل PyCharm، فيمكنك سحب ملف من جهاز الكمبيوتر الخاص بك إلى الشريط الجانبي للمشروع لنقل الملف.

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

نصيحة: في PyCharm، إذا كان الشريط الجانبي للمشروع مفتوحًا، يمكنك النقر فوق اسم المشروع (السطر العلوي) ثم إظهار القائمة السياقية بنقرة تحكم (Mac) أو النقر بزر الماوس الأيمن (Windows / Linux). سيكون أحد الخيارات هو إظهار في Finder أو إظهار في Explorer اعتمادًا على نظام التشغيل الذي تستخدمه.

قراءة البيانات من ملف نصي

في جميع البرامج التي كتبتها حتى الآن، كانت جميع البيانات التي استخدمتها عبارة عن بيانات كتبتها في برنامجك مباشرةً. نادرًا ما يكون هذا هو الحال في الحياة الواقعية. في العديد من تطبيقات البرمجة، سيحتاج برنامجك إلى الوصول إلى البيانات المتاحة في شكل آخر. في النصف الثاني من هذا الكتاب، ستقضي الكثير من الوقت في البحث عن طرق مختلفة لاستيراد البيانات والوصول إليها من مصادر خارجية. هنا، ستلقي نظرة على إحدى الطرق الأساسية والمفيدة للغاية لاستيراد البيانات، وهي: القراءة من ملف نصي reading from a text file.

نص الكتاب موجود في ملف نصي pride_and_prejudice.txt. هذا ملف نصي عادي يمكنك النقر فوقه نقرًا مزدوجًا فوق أي جهاز كمبيوتر وفتحه وقراءته باستخدام برنامج قياسي على جهاز الكمبيوتر الخاص بك. ستحتاج إلى إحضار محتويات هذا الملف إلى بيئة Python الخاصة بك قبل أن تتمكن من بدء العمل معه.

مثلما يمكنك فتح ملف في نظام تشغيل جهاز الكمبيوتر الخاص بك، يمكنك فتح الملف داخل برنامج Python الخاص بك. حان الوقت لفتح نص Python جديد والبدء:

open("pride_and_prejudice.txt")

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

file = open("pride_and_prejudice.txt")

print(file)

ناتج هذا الكود ليس تمامًا كما قد تتوقعه:

<_io.TextIOWrapper name='pride_and_prejudice.txt' mode='r' encoding='UTF-8'>

دعونا نتجاهل هذا الآن. يمكنك التفكير في الكائن الذي تم إرجاعه من open () باعتباره مؤشرًا للملف الذي فتحته للتو. نوع البيانات لهذا الكائن هو _io.TextIOWrapper. من الأفضل تجاهل أي شيء يبدو غامضًا وغير قابل للقراءة في الوقت الحالي! بدلاً من ذلك، يمكنك معرفة الأشياء التي يمكنك القيام بها بالمتغير “ملف” file عن طريق كتابته في البرنامج النصي الخاص بك متبوعًا بنقطة. إذا كنت تستخدم IDE مثل PyCharm، فسترى قائمة بالطرق التي ستظهر لك للاختيار من بينها. إحدى هذه الطرق هي القراءة read():

file = open("pride_and_prejudice.txt")

text = file.read()
print(text)

طريقة read() ستقرأ، كما خمنت ذلك، محتويات الملف. لقد قمت بتخزين البيانات التي تم إرجاعها بواسطة read () في متغيّر نصي آخر، وعندما تطبع نصًا، سترى محتويات الملف بالكامل الناتجة عن شفرتك. ربما يمكنك تخمين نوع بيانات المتغيّر النصي text:

file = open("pride_and_prejudice.txt")

text = file.read()
print(type(text))

ستجد أن الطريقة read() تُرجع سلسلة string. من هذه النقطة فصاعدًا، يتم تخزين محتوى الملف النصي في برنامجك كأحد أنواع البيانات الأساسية في Python. لقد قمت بإحضار بيانات من العالم الخارجي إلى برنامج بايثون الخاص بك.

لاحظ أن السطر الأول والأخير من الملف النصي ليسا جزءًا من الكتاب ولكنهما يمثلان المصدر لهذا الكتاب الإلكتروني مفتوح المصدر الذي نستخدمه. يمكنك إزالة هذه الأسطر من نسختك إذا كنت ترغب في ذلك.

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

file = open("pride_and_prejudice.txt")

text = file.read()
file.close()
print(text)

على الرغم من أن فتح الملف وإغلاقه باستخدام الدالة open () المضمّنة وطريقة الإغلاق close() هي الطريقة الأكثر وضوحًا، إلا أن الإصدارات الحديثة من Python قدّمت طريقة أفضل لضمان عدم بقاء أي ملفات مفتوحة. وذلك باستخدام الكلمة الرئيسية “مع” with:

with open("pride_and_prejudice.txt") as file:
    text = file.read()

print(text)

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

قراءة البيانات وتنظيفها

من وجهة نظر برنامجك، كل ما لديك حتى الآن هو سلسلة واحدة string. سلسلة واحدة طويلة جدًا. ما تحتاجه بشكل مثالي هو الكلمات الفردية. ستأتي إحدى طرق سلسلة Python لإنقاذك ومساعدتك في تنفيذ هذه المهمة. يمكنك استكشاف هذه الطريقة في وحدة التحكم Console:

>>> some_text = "This is a sentence which is stored as one single string"
>>> some_text.split()
['This', 'is', 'a', 'sentence', 'which', 'is', 'stored', 'as', 'one', 'single', 'string']

طريقة Split ()، التي تعمل على بيانات من نوع سلسلة string، تُرجع قائمة من السلاسل حيث تكون كل كلمة عنصرًا منفردًا داخل القائمة. بشكل افتراضي، تفصل الطريقة Split () السلسلة بناءً على مكان وجود مسافات في السلسلة. سترى كيفية استخدام split () بطريقة أكثر مرونة لاحقًا في هذا الكتاب.

يمكنك الآن استخدام هذه الطريقة في نص رواية كبرياء وتحامل:

with open("pride_and_prejudice.txt") as file:
    text = file.read()

words = text.split()
print(words)

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

في أي مشروع تجلب فيه البيانات من مصدر خارجي، ستحتاج إلى تنظيف البيانات clean the data قبل أن تتمكن من البدء في استخدامها. ما هو مطلوب لتنظيف البيانات سيعتمد على البيانات التي لديك وما تريد القيام به بالبيانات. في هذه الحالة، يعني تنظيف البيانات إزالة الأحرف الكبيرة Capital وإزالة علامات الترقيم بحيث تترك الكلمات بالأحرف الصغيرة فقط.

يمكنك البدء بإزالة كل الأحرف الكبيرة في النص. لقد استخدمت طريقة السلسلة lower() سابقًا في هذا الفصل والتي تقوم بتغيير السلسلة إلى نسخة ذات أحرف صغيرة بالكامل. لديك قائمة فيها كل عنصر عبارة عن سلسلة. يمكنك استخدام طريقة lower() على كل من هذه السلاسل باستخدام حلقة for.

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

with open("pride_and_prejudice.txt") as file:
    text = file.read()

text.lower()

words = text.split()
print(words)

هل نجح هذا؟ هل كل الكلمات في القائمة تطبعها بأحرف صغيرة؟

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

with open("pride_and_prejudice.txt") as file:
    text = file.read()

text = text.lower()

words = text.split()
print(words)

الإخراج من هذا الكود هو التالي (تم اقتطاع القائمة في الإخراج المعروض هنا لأغراض العرض):

['the', 'project', 'gutenberg', 'ebook', 'of', 'pride', 'and', 'prejudice,', 'by', 'jane', 'austen', 'pride', 'and', 'prejudice', 'by', 'jane', 'austen', 'chapter', '1', 'it', 'is', 'a', 'truth', 'universally', 'acknowledged,', 'that', 'a', 'single', 'man', 'in', 'possession', 'of', 'a', 'good', 'fortune,', 'must', 'be', 'in', 'want', 'of', 'a', 'wife.', 'however', 'little', 'known', 'the', 'feelings', 'or', 'views', 'of', 'such', 'a', 'man', 'may', 'be', 'on', 'his', 'first', 'entering', ...

تبرز الكلمات القليلة الأولى في القائمة بعض الإدخالات التي بها مشكلات. إذا نظرت إلى المدخلات الثامن والرابع عشر، فسترى كلمة “prejudice,” وكلمة “prejudice”. إنهما نفس الكلمة، لكن ليس بالنسبة لبايثون:

>>> "prejudice," == "prejudice"
False

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

with open("pride_and_prejudice.txt") as file:
    text = file.read()

text = text.lower()
text = text.replace(",", " ")

words = text.split()
print(words)

أنت تستبدل جميع الفواصل بمسافة في النص بالكامل قبل تقسيم السلسلة إلى قائمة من الكلمات:

['the', 'project', 'gutenberg', 'ebook', 'of', 'pride', 'and', 'prejudice', 'by', 'jane', 'austen', 'pride', 'and', 'prejudice', 'by', 'jane', 'austen', 'chapter', '1', 'it', 'is', 'a', 'truth', 'universally', 'acknowledged', 'that', 'a', 'single', 'man', 'in', 'possession', 'of', 'a', 'good', 'fortune', 'must', 'be', 'in', 'want', 'of', 'a', 'wife.', 'however', 'little', 'known', 'the', 'feelings', 'or', 'views', 'of', 'such', 'a', 'man', 'may', 'be', 'on', 'his', 'first', 'entering', ...

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

with open("pride_and_prejudice.txt") as file:
    text = file.read()

text = text.lower()
for punctuation_mark in ".,?!;:-":
    text = text.replace(punctuation_mark, " ")

words = text.split()
print(words)

يمكنك إجراء تكرار الحلقات loop فوق أي نوع بيانات قابل للتكرار iterable، والسلسلة قابلة للتكرار. لذلك، يمكنك كتابة سلسلة بها عدة علامات ترقيم في عبارة for. سيكون متغير علامة الترقيم punctuation_mark مساويًا لـ “.” في التكرار الأول، وبالتالي فإن طريقة الاستبدال replace() ستحل محل جميع النقاط (.) بمسافة. في التكرار الثاني لحلقة for، ستكون علامة الترقيم punctuation_mark مساوية للفاصلة “،”، وهكذا لبقية علامات الترقيم.

يمكنك مسح إخراج الكود للتأكد من عدم وجود أي من علامات الترقيم التي قمت بإدراجها في عبارة حلقة for loop.

أنت الآن بحاجة إلى محاولة التفكير في جميع علامات الترقيم الممكنة. أو اعتمد فقط على السلسلة الجاهزة للاستخدام التي تمتلكها Python في إحدى الوحدات النمطية المضمّنة بها والتي تسمى السلسلة string. يمكنك استكشاف هذا في وحدة التحكم أولاً:

>>> import string
>>> string.punctuation
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'

تحتوي هذه الوحدة على سلسلة تسمى علامات الترقيم punctuation  تحتوي على جميع علامات الترقيم المتاحة. يمكنك الآن إزالة جميع علامات الترقيم من رواية كبرياء وتحامل Pride and Prejudice:

import string

with open("pride_and_prejudice.txt") as file:
    text = file.read()

text = text.lower()
for punctuation_mark in string.punctuation:
    text = text.replace(punctuation_mark, " ")

words = text.split()
print(words)

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

لقد تعلمت التعليق commenting  كطريقة واحدة لجعل شفرتك أكثر قابلية للقراءة. يمكنك إضافة بعض التعليقات الموجزة والموضوعة جيدًا في التعليمات البرمجية الخاصة بك:

import string

with open("pride_and_prejudice.txt") as file:
    text = file.read()

# Clean data by removing capitalisation and punctuation marks
text = text.lower()
for punctuation_mark in string.punctuation:
    text = text.replace(punctuation_mark, " ")

# Split string into a list of strings containing all the words
words = text.split()

ليس هناك عدد مثالي من التعليقات ستحتاج إلى إدخاله. هذا يعتمد على تفضيلاتك وأسلوبك. اخترت عدم التعليق على القسم الأول حيث يتم فتح الملف حيث يبدو الرمز واضحًا بدرجة كافية. التعليق في الكتلة الأخيرة موجود لتذكير القارئ بأن words  هي قائمة من السلاسل. مع نمو البرامج، قد تفقد تتبع نوع البيانات الذي تخزّنه المتغيّرات المختلفة، لذلك قد يكون تعليق مثل هذا مفيدًا.

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

تحليل تكرارات الكلمات

لقد قمت بتحويل النص إلى قائمة كلمات تم تنظيفها. كيف تنطلق من هذه النقطة؟ دعنا نسافر عبر الزمن إلى عصر ما قبل الكمبيوتر، ويمكنك أن تسأل نفسك: “كيف يمكنني تنفيذ هذه المهمة باستخدام القلم والورق إذا كان لدي الكثير من الوقت؟”

حاول تدوين الخطوات التي ستحتاجها قبل القراءة.

فيما يلي الخطوات التي عليك القيام بها لحل هذه المشكلة:

  • انظر إلى الكلمة الأولى واكتبها على ورقة. أضف الرقم 1 بجوارها لتظهر أنها المرة الأولى التي تعثر فيها على هذه الكلمة.
  • انتقل إلى الكلمة التالية. هل سبق لك أن واجهت هذه الكلمة من قبل؟
    • إذا لم تكن هذه هي المرة الأولى التي تعثر فيها على هذه الكلمة، فابحث عنها في ورقتك وأضف 1 إلى الرقم المجاور لها.
    • إذا كانت هذه هي المرة الأولى التي تصادف فيها هذه الكلمة، فاكتبها في أسفل الورقة وأضف 1 بجانبها.
  • كرر هذه الخطوات حتى تنتهي من قراءة الكتاب بأكمله.
  • توقف كثيرًا لتحضر لنفسك قهوة قوية!

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

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

من المفيد إنشاء نسخة وهمية من القاموس الذي تريده في وحدة التحكم لتصوّره حتى تتمكن من تجربته حسب الحاجة:

>>> some_words = {"hello": 3, "python": 8, "bye": 1}

>>> some_words["computer"] = 2  # Add new word
>>> some_words
{'hello': 3, 'python': 8, 'bye': 1, 'computer': 2}

>>> some_words["python"] = some_words["python"] + 1  # Increment count for existing word
>>> some_words
{'hello': 3, 'python': 9, 'bye': 1, 'computer': 2}

الآن أنت تعرف الشكل الذي سيتخذه قاموسك وكيفية إضافة كلمات جديدة وزيادة عدد الكلمات الموجودة بالفعل في القاموس، يمكنك الرجوع إلى البرنامج النصي P&P. ستحتاج أولاً إلى إنشاء متغير بقاموس فارغ مخزّن فيه:

# follows on from code you've already written above
# which reads from file and cleans the data

word_frequencies = {}

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

# follows on from code you've already written above
# which reads from file and cleans the data

word_frequencies = {}

for word in words:
    if word not in word_frequencies.keys():
        word_frequencies[word] = 1

من الممارسات الجيدة استخدام النسخة المفردة من الاسم الذي استخدمته لقائمتك كمتغير تحدده في تعليمة الحلقة for، كما يلي: for word in words:

هذا يجعل الكود أكثر قابلية للقراءة.

لكل كلمة في قائمة الكلمات for word in words، أنت تتحقق مما إذا كانت الكلمة موجودة بالفعل في قاموس word_frequencies. يمكنك تفكيك ما يحدث في عبارة if بالرجوع إلى القاموس الوهمي الذي أنشأته في وحدة التحكم:

>>> # using the same variable some_words you created and modified earlier
>>> some_words
{'hello': 3, 'python': 9, 'bye': 1, 'computer': 2}

>>> some_words.keys()
dict_keys(['hello', 'python', 'bye', 'computer'])

>>> 'python' in some_words
True

>>> 'monday' in some_words
False

>>> 'monday' not in some_words
True

تقوم طريقة القاموس keys() بإرجاع تسلسل يحتوي على جميع المفاتيح الموجودة في القاموس. يمكن استخدام الكلمة الأساسية “في” in لإرجاع صواب true أو خطأ false بناءً على ما إذا كانت السلسلة التي تستخدمها موجودة “في” التسلسل الذي يحتوي على جميع المفاتيح. لم تصادفك الكلمة الرئيسية “ليس” not حتى الآن، ولكن يمكنك تخمين ما تفعله من المثال أعلاه!

بقيت خطوة واحدة أخيرة. ستحتاج إلى إضافة عبارة else لتتبع if لحساب الكلمات الموجودة بالفعل في القاموس. سيحدث هذا عندما تأتي الحلقة for عبر إحدى الكلمات للمرة الثانية والثالثة وهكذا:

# follows on from code you've already written above
# which reads from file and cleans the data

word_frequencies = {}

# Loop through list of words to populate dictionary
for word in words:
    if word not in word_frequencies.keys():
        word_frequencies[word] = 1
    else:
        word_frequencies[word] = word_frequencies[word] + 1

حان الوقت للكشف عما يحتويه متغير word_frequencies من خلال طباعته:

print(word_frequencies)

الناتج عبارة عن قاموس طويل جدًا (تم عرض نسخة مبتورة فقط هنا، ولكن ستتمكن من رؤية القاموس بالكامل في إصدارك):

{'the': 4333, 'project': 3, 'gutenberg': 2, 'ebook': 2, 'of': 3614, 'pride': 50, 'and': 3587, 'prejudice': 8, 'by': 638, 'jane': 294, 'austen': 3, 'chapter': 61, '1': 1, 'it': 1535, 'is': 860, 'a': 1954, 'truth': 27, 'universally': 3, 'acknowledged': 20, 'that': 1579, 'single': 12, 'man': 151, 'in': 1880, 'possession': 9, 'good': 201, 'fortune': 39, 'must': 308, 'be': 1241, 'want': 44, 'wife': 47, 'however': 134, 'little': 189, 'known': 58, 'feelings': 86, 'or': 299, ...

لديك قاموس بكل الكلمات الموجودة في الكتاب وعدد مرات استخدام كل كلمة. يمكنك معرفة عدد الكلمات الفريدة الموجودة في الكتاب:

print(len(word_frequencies))

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

“هل يمكنني فرز القاموس بناءً على تكرارات الكلمات؟” هو سؤال شائع يُطرح عليّ. الجواب نعم. على الرغم من أنه من الممكن القيام بذلك مباشرة داخل Python، إلا أنك ستتبع نهجًا مختلفًا في القسم التالي، والذي سيسمح لك بعد ذلك بالفرز باستخدام أداة ربما تكون على دراية بها بالفعل.

التكرار من خلال قاموس

في وقت سابق من هذا الفصل، عندما تعرّفت على القواميس لأول مرة، رأيت أنه يمكنك عمل حلقة من خلال قاموس. ومع ذلك، يمكنك فقط التكرار من خلال مفاتيح القاموس. دعونا نلقي نظرة على طريقة أفضل للتكرار خلال القاموس باستخدام حلقة for.

التفاف قصير جدا أولاً. قم بإنشاء مجموعة tuple تحتوي على عنصرين:

>>> numbers = (5, 2)

يمكنك تفكيك unpack هذه المجموعة إلى متغيرين منفصلين:

>>> first, second = numbers

>>> first
5
>>> second
2

يعمل التفكيك Unpacking  مع التسلسلات الأخرى أيضًا، وليس فقط مع المجموعات tuples. الآن، دعنا نلقي نظرة على طريقة قاموس أخرى تسمى العناصر items(). يمكنك استخدام نفس القاموس الوهمي الذي استخدمته سابقًا عند إجراء التجربة في وحدة التحكم:

>>> some_words = {'hello': 3, 'python': 9, 'bye': 1, 'computer': 2}
>>> some_words.items()
dict_items([('hello', 3), ('python', 9), ('bye', 1), ('computer', 2)])

تقوم هذه الطريقة بإرجاع كائن من النوع dict_items. لا يهم ما هو نوع البيانات هذا. ما يهم هو أنه تسلسل يتم فيه تجميع كل زوج من المفاتيح والقيمة في مجموعة. يمكنك أن ترى أن العنصر الأول في هذا التسلسل هو (‘hello’, 3) وهكذا. لذلك يمكنك استخدام التسلسل الذي تم إرجاعه من طريقة العناصر items() في حلقة for:

>>> for something in some_words.items():
...     print(something)
...     
('hello', 3)
('python', 9)
('bye', 1)
('computer', 2)

يمكنك الآن إجراء حلقة من خلال القاموس والوصول إلى جميع المعلومات الموجودة في القاموس، وليس المفاتيح فقط. يحتوي المتغير something  على مجموعة مكونة من عنصرين. لذا يمكنك تفكيك هذه المجموعة. في الواقع، يمكنك القيام بالتفكيك مباشرة في جملة for:

>>> for word, frequency in some_words.items():
...     print(word)
...     print(frequency)
...     
hello
3
python
9
bye
1
computer
2

أنت الآن تقوم بتعريف متغيرين في العبارة for، word وfrequency، وتعيين قيم لهما عن طريق تفكيك المجموعات في التسلسل الذي تعيده الطريقة some_words.items(). لذلك، يمكنك الوصول إلى كل من المفتاح والقيمة في كل تكرار للحلقة for.

كتابة البيانات في جدول بيانات

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

>>> file = open("test_file.txt", "w")
>>> file.write("Good Morning.\nI'm writing this to a file. Hurray!")
49
>>> file.close()

لقد فتحت الملف بطريقة مماثلة كما في السابق، مع اختلاف واحد. هناك الآن وسيطان في الدالة open():

  • اسم الملف كسلسلة، بما في ذلك امتداد الملف
  • سلسلة ثانية تعرض الوضع mode  الذي تريد فتح الملف به

السلسلة “w” تعني “الكتابة” write. أنت تفتح الملف في وضع الكتابة write-mode. لم يكن الملف test_file.txt موجودًا في مجلدك، ولكن فتح ملف في وضع الكتابة يؤدي إلى إنشاء الملف إذا لم يكن موجودًا.

كلمة تحذير: إذا فتحت ملفًا موجودًا بالفعل باستخدام الوسيطة “w”، فسيتم استبدال ملفك. تأكد دائمًا من أن لديك نسخ احتياطية من أي ملفات تريد استخدامها حتى تتجنّب حذف محتويات ملفك عن طريق الخطأ. الوضع الآخر المتاح مع open () هو “a”، والذي يرمز إلى إلحاق append. يسمح لك الملف المفتوح في وضع الإلحاق بالكتابة في نهاية الملف الحالي دون حذف محتوياته الحالية.

في وقت سابق من هذا الفصل، عندما فتحت pride_and_prejudice.txt، استخدمت اسم الملف كوسيطة فقط عند استدعاء open (). في هذه الحالة، يكون الوضع الافتراضي هو “r”، والذي يرمز للقراءة read. تم فتح الملف في وضع القراءة فقط read-only الذي يحافظ على الملف الأصلي في مأمن من التعديل العَرَضي في كود Python الخاص بك.

إذا حددت موقع مجلد المشروع على جهاز الكمبيوتر، فستتمكن الآن من العثور على ملف جديد يسمى test_file.txt. سيظهر لك فتح هذا الملف ملفًا نصيًا يحتوي على ما يلي:

Good Morning.
I'm writing this to a file. Hurray!

إذا نظرت للخلف في السلسلة التي استخدمتها كوسيطة إدخال في open ()، ستلاحظ الحرف “\ n” بعد النقطة الأولى. هذا يسمى حرف الهروب escape character. تبدأ أحرف الهروب في السلاسل بشرطة مائلة للخلف. يمثل حرف الهروب هذا حرف السطر الجديد. ستلاحظ أن “\ n” لم تتم طباعته في الملف النصي ولكن يبدأ سطر جديد عند هذه النقطة.

ربما لاحظت أيضًا أن طريقة write() أعادت العدد الصحيح 49. هذا هو عدد الأحرف المكتوبة في الملف. لن تحتاج إلى هذه القيمة، لذا يمكنك تجاهلها.

يمكنك الآن العودة إلى مشروع P&P، وأنت على استعداد لتصدير Export البيانات إلى جدول بيانات. ستستخدم تنسيق ملف CSV، وهو أكثر تنسيقات جداول البيانات وضوحًا. يشير CSV إلى اختصار comma-separated values أي قيم مفصولة بفواصل. ملف CSV هو ملف نصي قياسي له امتداد ملف .csv حيث يتم فصل كل قيمة بفاصلة، كما يوحي الاسم. هذه القيم هي محتويات كل خلية في جدول بيانات. سيفتح جهاز الكمبيوتر ملف CSV باستخدام برنامج جداول البيانات الافتراضي لديك.

الخطوات التي ستحتاجها لإنشاء جدول بيانات هي:

  • فتح ملفًا ممكّنًا للكتابة
  • كتابة سطر رأس جدول البيانات
  • استخدام حلقة من خلال القاموس word_frequencies وكتابة كل زوج من المفاتيح والقيمة إلى الملف
  • إغلاق الملف (إلا إذا كنت تستخدم تعليمة with)

يمكنك الآن ترجمة هذه الخطوات إلى لغة برمجة بايثون:

# follows on from existing code written earlier in P&P project

# Export words and frequencies to a CSV spreadsheet
file = open("words in Pride and Prejudice.csv", "w")
# Write header line
file.write("Word,Frequency\n")

# Loop through dictionary and write key-value pairs to csv
for word, frequency in word_frequencies.items():
    file.write(f"{word},{frequency}\n")
file.close()

قبل الحلقة، أنت تكتب السطر العلوي من جدول البيانات وهو صف العنوان. ستحتاج إلى تضمين حرف إقحام السطر الجديد “\ n” لتوضيح أن هذه هي نهاية السطر.

داخل حلقة for، أنت تستخدم سلسلة f-string لكتابة محتويات المتغيرات word وfrequency التي حددتها في العبارة for. هذه تمثل المفاتيح والقيم في القاموس. توجد أيضًا فاصلة تفصل بين القيم وحرف إقحام سطر جديد في النهاية.

يمكنك الآن العثور على ملف يسمى words في Pride and Prejudice.csv في مجلد المشروع الخاص بك. إذا قمت بالنقر نقرًا مزدوجًا فوق ملف CSV هذا كما تفعل مع أي ملف آخر تريد فتحه على جهاز الكمبيوتر الخاص بك، فسيتم فتحه في برنامج جداول البيانات القياسي لديك. إذا كنت ترغب في فرز الكلمات بناءً على تكراراتها، يمكنك القيام بذلك في برنامج جداول البيانات الخاص بك.

إليك الكود الكامل لمشروع P&P. في الإصدار أدناه، يتم استخدام الكلمة الرئيسية with لفتح ملف CSV للكتابة، بما يتماشى مع ما استخدمته سابقًا لقراءة الملف النصي وأفضل الممارسات الحديثة في Python. ومع ذلك، إذا كنت تفضّل استخدام الإصدار مع دالة الفتح الصريح open() والإغلاق الصريح close() في الوقت الحالي، فيمكنك القيام بذلك:

import string

####
# PART 1: read and clean data
with open("pride_and_prejudice.txt") as file:
    text = file.read()

# Clean data by removing capitalisation and punctuation marks
text = text.lower()
for punctuation_mark in string.punctuation:
    text = text.replace(punctuation_mark, " ")

# Split string into a list of strings containing all the words
words = text.split()

####
# PART 2: find words and their frequencies
word_frequencies = {}

# Loop through list of words to populate dictionary
for word in words:
    if word not in word_frequencies.keys():
        word_frequencies[word] = 1
    else:
        word_frequencies[word] = word_frequencies[word] + 1

####
# PART 3: Export words and frequencies to a CSV spreadsheet

with open("words in Pride and Prejudice.csv", "w") as file:
    # Write header line
    file.write("Word,Frequency\n")

    # Loop through dictionary and write key-value pairs to csv
    for key, value in word_frequencies.items():
        file.write(f"{key},{value}\n")

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

قائمة الفهم list comprehension

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

على سبيل المثال، لنفترض أن لديك قائمة بالأسماء، وتريد إنشاء قائمة جديدة تحتوي على نفس الأسماء بأحرف كبيرة:

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

new_names = []
for name in names:
    new_names.append(name.upper())

print(new_names)

تحتوي أسماء القائمة على خمسة عناصر بداخلها. يُظهر ناتج هذا الكود قائمة تحتوي على الإصدارات بالأحرف الكبيرة من هذه الأسماء:

['JOHN', 'MARY', 'ISHAN', 'SUE', 'GABY']

الخطوات التي تستخدمها في هذه الخوارزمية هي:

  1. إنشاء قائمة فارغة جديدة
  2. تكرار خلال القائمة الأصلية باستخدام حلقة for
  3. إجراء العملية المطلوبة على كل عنصر من عناصر القائمة الأصلية وإلحاق النتيجة بالقائمة الجديدة

هذه الخطوات الثلاث شائعة جدًا في البرمجة لدرجة أن بايثون Python لديها طريقة أقصر لتحقيق نفس النتيجة. يمكنك استخدام قائمة الفهم List Comprehensions:

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

new_names = [name.upper() for name in names]

print(new_names)

لقد استبدلت الآن الأسطر الثلاثة في الشفرة الأصلية التي تنشئ القائمة الفارغة ثم تعبئتها باستخدام حلقة for بسطر واحد من التعليمات البرمجية. هذا السطر هو قائمة الفهم list comprehension. دعونا نلقي نظرة على العناصر المختلفة في هذا السطر:

  • أنت تقوم بإنشاء متغير جديد يسمى new_names بالطريقة المعتادة مع عامل الإسناد =.
  • على الجانب الأيمن من علامة =، لديك الأقواس المربعة التي تشير إلى القائمة.
  • يشير الجزء الأول الموجود داخل الأقواس المربعة إلى المتغير name غير الموجود بعد. ستصل إلى هذا في النقطة التالية. يتم تطبيق طريقة upper() على كل ما يتم تخزينه في المتغير name ووضعه في القائمة.
  • بعد الجزء الأول داخل فهم القائمة، لديك عبارة for. عبارة for هذه مطابقة لتعليمة for التي استخدمتها في حلقة for الأصلية.

هناك طريقة أخرى لإلقاء نظرة على ما يحدث في قائمة الفهم هذه وهي ترجمة هذا السطر إلى اللغة الطبيعية البسيطة الإنجليزية أو العربية مثلاً. ستقرأ الترجمة على النحو التالي: أنشئ قائمة بالاسم new_names واملأها بالمخرجات من name.upper() لكل اسم name في قائمة الأسماء list of names.

كفاءة قائمة الفهم

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

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

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

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

قائمة الفهم مع الجمل الشرطية

لنفترض أنك في قائمتك الجديدة، ترغب فقط في الحصول على النسخ الكبيرة للأسماء المكونة من أربعة أحرف. في الإصدار الكلاسيكي من الخوارزمية، يمكنك إضافة عبارة if إلى حلقة for:

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

new_names = []
for name in names:
    if len(name) == 4:
        new_names.append(name.upper())

print(new_names)

فقط عندما يكون طول الاسم مساويًا لـ 4، ستضيف الإصدار الكبير من الاسم إلى القائمة الجديدة. هذا يعطي الناتج التالي:

['JOHN', 'MARY', 'GABY']

يمكنك تحقيق نفس النتيجة مع قائمة الفهم:

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

new_names = [name.upper() for name in names if len(name) == 4]

print(new_names)

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

تقرأ الترجمة الآن: قم بإنشاء قائمة تسمى new_names واملأها بالمخرجات من name.upper() لكل اسم name في قائمة الأسماء names إذا كان طول الاسم len(name) هو 4. الجزء الذي تم إضافته موضح بالخط العريض. يمكنك أن ترى أن كود Python وترجمته الإنجليزية (أو العربية) ليسا مختلفين!

دعونا نضيف إضافة واحدة أخيرة. إذا لم يكن الاسم مكونًا من أربعة أحرف، فأنت تريد وضع السلسلة “xxxx” في القائمة بدلاً من الاسم. في الإصدار الأصلي من الكود، يمكنك إضافة الكلمة else إلى عبارة if:

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

new_names = []
for name in names:
    if len(name) == 4:
        new_names.append(name.upper())
    else:
        new_names.append("xxxx")

print(new_names)

يعطي هذا الرمز النتيجة التالية:

['JOHN', 'MARY', 'xxxx', 'xxxx', 'GABY']

يمكن أيضًا استخدام قائمة الفهم لتحقيق ذلك:

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

new_names = [name.upper() if len(name) == 4 else "xxxx" for name in names]

print(new_names)

الآن كلمة if متبوعة بكلمة else في قائمة الفهم. ربما لاحظت أن عبارة if / else تسبق عبارة for في قائمة الفهم هذه. ومع ذلك، في حالة عدم وجود كلمة else، كما في المثال السابق، جاءت عبارة if بعد عبارة for.

إذا كان هذا هو النص الرئيسي، فسأحيلك إلى مقتطف لشرح سبب ذلك، لأنه ليس مفتاحًا لفهم قوائم الفهم. ومع ذلك، فأنت تقرأ بالفعل مقتطفًا! الإجابة المختصرة هي أن تركيبة if / else هي نفسها عامل بايثون يسمى العامل الثلاثي. لذلك، فإن المصطلح الذي يسبق عبارة for هو: name.upper() if len(name) == 4 else “xxxx”، وهو عبارة Python صالحة بحد ذاتها. ومع ذلك، لن أناقش عامل التشغيل الثلاثي بمزيد من التفصيل الآن.

تركيبات فهم أخرى

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

إذا كنت تريد إنشاء قاموس تتكون فيه المفاتيح من كل اسم في قائمة الأسماء، وتكون قيمة كل مفتاح هي طول الاسم، يمكنك كتابة الكود التالي:

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

name_lengths = {}
for name in names:
    name_lengths[name] = len(name)

print(name_lengths)

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

{'John': 4, 'Mary': 4, 'Ishan': 5, 'Sue': 3, 'Gaby': 4}

يمكنك تحقيق نفس المخرجات باستخدام قاموس الفهم dictionary comprehension:

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

name_lengths = {name: len(name) for name in names}

print(name_lengths)

تتضمّن العبارة الموجودة قبل الكلمة الأساسية for الآن المفتاح key والقيمة value مفصولة بنقطتين، كما هو الحال في جميع القواميس.

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

قد تميل إلى تجربة نفس الشيء مع المجموعة tuple:

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

new_names = (name.upper() for name in names)

print(new_names)

الناتج من هذا الرمز يعطي النتيجة التالية:

<generator object <genexpr> at 0x7fe5ee5824a0>

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

names = ["John", "Mary", "Ishan", "Sue", "Gaby"]

new_names = tuple(name.upper() for name in names)

print(new_names)

وهذا بالفعل يعطي tuple:

('JOHN', 'MARY', 'ISHAN', 'SUE', 'GABY')

يمكن أن تبدو تركيبات الفهم Comprehensions غريبة بعض الشيء في البداية. ومع ذلك، بمجرد أن تعتاد عليها، ستراها طريقة ملائمة ومفيدة لكتابة تعليمات برمجية أكثر إتقانًا وفعالية. وسيوفرون لك بضعة أسطر من التعليمات البرمجية أيضًا!

الخلاصة

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

لقد غطيت في هذا الفصل:

  • ما الفرق بين أنواع البيانات القابلة للتكرار iterable وغير القابلة للتكرار non-iterable
  • ما هو الفرق بين أنواع البيانات القابلة للتغيير mutable  وغير القابلة للتغيير immutable
  • كيفية استخدام الطرق methods المرتبطة بأنواع البيانات
  • كيفية استخدام المجموعات tuples والقواميس dictionaries
  • كيف تقرأ read  البيانات من ملف
  • كيف تكتب write البيانات إلى ملف
  • كيفية استخدام قائمة الفهم list comprehension وكفائتها، واستخدامها في الجمل الشرطية

ستكون محطتك التالية في هذه الرحلة مليئة بالأخطاء errors والثغرات bugs! يركّز الفصل التالي على الاختلافات بين الأخطاء والثغرات وتعلّم كيفية التعامل مع الأخطاء وكيفية البحث عن الثغرات وإصلاحها.

المصدر

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