تعلم البرمجة مع Python


الدرس: إنشاء قوائم و tuples (2/2)


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

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

انطلاق !

بين السلاسل والقوائم



سنرى طريقة لتحويل السلاسل إلى قوائم والعكس صحيح.

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

من السلاسل إلى القوائم


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

إنه معقد بعض الشيء ويبدو ملتويًا جدًا ... لكن انظر بدلاً من ذلك:


>>> ma_chaine = "Bonjour à tous"
>>> ma_chaine.split(" ")
['Bonjour', 'à', 'tous']
>>>  
يتم تمرير split سلسلة تحتوي على مسافة واحدة كمعامل للطريقة . تعيد الطريقة قائمة تحتوي على الكلمات الثلاث لجملتنا القصيرة. كل كلمة في مربع في القائمة.

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

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

من القوائم إلى السلاسل


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

السلسلة join ( "Join" باللغة الإنجليزية) . تركيبها مثير للدهشة بعض الشيء:

>>> ma_liste = ['Bonjour', 'à', 'tous']
>>> " ".join(ma_liste)
'Bonjour à tous'
>>>
 
كمعامل للطريقة join ، نقوم بتمرير قائمة السلاسل التي نريد "إعادة تشكيلها" . ستعمل الطريقة على الكائن الذي يستدعيه ، وهنا سلسلة أحرف تحتوي على مسافة واحدة. ستقوم بإدخال هذه السلسلة بين كل زوج من السلاسل في القائمة ، مما يمنحنا في النهاية سلسلة البداية.

ألن يكون من الأسهل أو الأكثر منطقية إنشاء طريقة قائمة ، مع الأخذ كمعامل السلسلة التي تصنع الوصلة(join)؟

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

تطبيق عملي


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

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


>>> afficher_flottant(3.99999999999998)
'3,999'
>>> afficher_flottant(1.5)
'1,5'
>>>
 
هذا هو التصحيح الذي أقترحه عليك:

def afficher_flottant(flottant):
    """ الدالة تأخذ عدد عشري كمعامل وتعيد سلسلة أحرف تمثل اقتطاع هذا الرقم. يجب ألا يزيد طول الجزء العائم عن 3 أحرف.

    بالإضافة إلى ذلك ، سنستبدل الفاصلة العشرية بالنقطة """
    
    if type(flottant) is not float:
        raise TypeError("يجب أن تكون المعلمة المتوقعة عددًا عشريًاt")
    flottant = str(flottant)
    partie_entiere, partie_flottante = flottant.split(".")
    return ",".join([partie_entiere, partie_flottante[:3]])
 
من خلال التأكد من أن النوع الذي تم تمريره في المعلمة هو عائم بالفعل ، فإننا نضمن أنه لن يكون هناك خطأ عند تقسيم السلسلة. نحن على يقين من أنه سيكون هناك بالضرورة جزء صحيح وجزء عائم مفصول بنقطة ، حتى لو كان الجزء العائم مكونًا فقط من 0. إذا لم تقم بإدارته بنفسك ، قم بالدراسة حسنًا ، هذا الحل ، ليس بالضرورة واضحًا للوهلة الأولى. نقدم لك عددًا من الآليات التي رأيتها منذ وقت ليس ببعيد ، لذا حاول فهمها.

قوائم الوظائف والمعلمات



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

لاحظ ، مع ذلك ، أن هذه النقطة حساسة للغاية. إذا لم تتمكن من معرفة ذلك تمامًا ، فاترك هذا القسم جانبًا ، فلن يعاقبك.

الوظائف التي لا يعرف عدد المعلمات الخاصة بها مسبقًا


يجب أن تفكر على الفور في الوظيفة print : نقوم بتمرير قائمة من المعلمات التي ستعرضها ، بالترتيب الذي يتم وضعها به ، مفصولة بمسافة (أو أي محدد آخر تم اختياره).

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


def fonction(*parametres):
 
نضع نجمة *أمام اسم المعلمة التي ستستضيف قائمة الوسائط. دعونا نرى كيف تبدو بدقة أكبر:

>>> def fonction_inconnue(*parametres):
...     """ اختبار دالة يمكن استدعاؤها بعدد متغير من المعلمات """
...     
...     print("J'ai reçu : {}.".format(parametres))
... 
>>> fonction_inconnue() # نستدعي الدالة بدون معلمات
J'ai reçu : ().
>>> fonction_inconnue(33)
J'ai reçu : (33,).
>>> fonction_inconnue('a', 'e', 'f')
J'ai reçu : ('a', 'e', 'f').
>>> var = 3.5
>>> fonction_inconnue(var, [4], "...")
J'ai reçu : (3.5, [4], '...').
>>>
 
وأعتقد أن هذا يكفي. كما ترى ، يمكننا fonction_inconnue تسميتها بعدد غير محدد من المعلمات ، من 0 إلى ما لا نهاية (حسنًا ، من الناحية النظرية). يؤدي تحديد نجمة *أمام اسم المعلمة إلى قيام Python بوضع جميع معلمات الوظيفة في مجموعة ، والتي يمكن معالجتها حسب الرغبة.

وما المعالم المذكورة في القصة؟ كيف يتم إدخالها في المجموعة؟

هم ليسوا . إذا كنت اكتب fonction_inconnue(couleur="rouge")، سوف تحصل على خطأ: fonction_inconnue() got an unexpected keyword argument 'couleur' . سنرى في الفصل التالي كيفية التقاط هذه المعلمات المسماة.

يمكنك بالطبع تحديد وظيفة مع العديد من المعلمات التي يجب توفيرها مهما كانت ، متبوعة بقائمة من المعلمات المتغيرة:


def fonction_inconnue(nom, prenom, *commentaires):
 
في هذا المثال لتعريف الوظيفة ، يجب تحديد اسم واسم أول ، ثم تعلق على ما تريد ، بدون معلمات ، واحد ، اثنان ... ما تريد.

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

في الأساس ، هذا واضح. لا يمكنك الحصول على تعريف دالة مثل def fonction_inconnue(*parametres, nom, prenom) . من ناحية أخرى ، إذا كنت تريد تسمية المعلمات ، فعليك وضعها بعد هذه القائمة. تعتبر المعلمات المسماة استثناءً قليلاً لأنها لن تظهر في المجموعة الناتجة على أي حال. دعنا نرى على سبيل المثال تعريف الوظيفة print :


print(value, ..., sep=' ', end='\n', file=sys.stdout)
 
دعونا لا نتعامل مع المعلمة الأخيرة. يحدد الواصف الذي print ترسل إليه بياناته ؛ بشكل افتراضي ، هذه هي الشاشة.

من أين تأتي هذه الأشكال البيضاوية في الإعدادات؟

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


def print(*values, sep=' ', end='\n', file=sys.stdout):
 
تمرين صغير: قم بوظيفة afficher مماثلة print ، أي أخذ عدد غير محدد من المعلمات ، وعرضها عن طريق فصلها باستخدام المعلمة المسماة sep وإنهاء العرض بالمتغير fin . لدينا وظيفة afficher لن يكون لها اي المعلمات file . بالإضافة إلى ذلك ، يجب أن يتم print عرضه (لا نعرف بعد طرقًا أخرى للقيام بذلك). القيد الوحيد هو أن الاستدعاء print يجب أن يحتوي على معلمة واحدة غير مسماة. بمعنى آخر ، قبل الاتصال بـ print، يجب أن تكون السلسلة قد تم تنسيقها بالفعل وجاهزة للعرض.

لتوضيح الأمر ، أقدم لك تعريف الوظيفة ، بالإضافة إلى التعريف  docstring الذي كتبته:


def afficher(*parametres, sep=' ', fin='\n'):
    """ الوظيفة المسؤولة عن إعادة إنتاج سلوك الطباعة.
    
    يجب أن ينتهي الأمر بالاتصال بالطباعة لعرض النتيجة.
    لكن يجب أن تكون الإعدادات مهيأة بالفعل. 
    يجب أن نمرر سلسلة واحدة للطباعة ، مع تحديد عدم وضع أي شيء في النهاية :

    print(chaine, end='')"""
    
    # المعلمات في شكل مجموعة
    # لكننا بحاجة إلى تحويلها
    # لكن لا يمكنك تعديل tuple
    # لدينا العديد من الاحتمالات ، وهنا أختار تحويل المجموعة إلى قائمة    
    parametres = list(parametres)
    # سنبدأ بتحويل جميع القيم إلى سلاسل
    # وإلا سنواجه بعض المشاكل أثناء الانضمام    
    for i, parametre in enumerate(parametres):
        parametres[i] = str(parametre)
    # تحتوي قائمة المعلمات على سلاسل فقط
    # الآن سنبني السلسلة النهائية    chaine = sep.join(parametres)
    # أضف معلمة النهاية في نهاية السلسلة
    chaine += fin
    # نعرض المجموعة
    print(chaine, end='')
 
آمل ألا يكون الأمر صعبًا للغاية وإذا ارتكبت أي أخطاء يمكنك اكتشافها.

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

تحويل قائمة إلى معلمات دالة


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


>>> liste_des_parametres = [1, 4, 9, 16, 25, 36]
>>> print(*liste_des_parametres)
1 4 9 16 25 36
>>>
 
إنها ليست مذهلة للغاية ومع ذلك فهي ميزة قوية جدًا للغة. هناك ، لدينا قائمة تحتوي على معلمات ونحولها إلى قائمة معلمات الوظيفة print . لذلك بدلاً من عرض القائمة نفسها ، نعرض جميع الأرقام مفصولة بمسافات. هو بالضبط كما لو كنت قد فعلت print(1, 4, 9, 16, 25, 36).

لكن ما الفائدة؟ لا يتغير كثيرًا ، ومن النادر التقاط معلمات الوظيفة في قائمة ، أليس كذلك؟

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

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

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

قائمة التفاهمات



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

طريق بسيط


وlist comprehensions للتمرير من خلال قائمة من خلال العودة لثانية واحدة أو تعديل أو تصفيتها. في الوقت الحالي ، سنرى تعديلًا بسيطًا.


>>> liste_origine = [0, 1, 2, 3, 4, 5]
>>> [nb * nb for nb in liste_origine]
[0, 1, 4, 9, 16, 25]
>>>
 
دعنا نلقي نظرة على السطر 2 من هذا الكود. كما قد تكون خمنت ، فهذا يعني بلغة أكثر تقليدية "تربيع جميع الأرقام الموجودة في القائمة الأصلية". نجد بالترتيب ، بين الأقواس التي هي محددات تعليمات فهم القائمة:
  • nb * nb : القيمة المرجعة. في الوقت الحالي ، لا نعرف ما هو المتغير nb، ولكننا نعلم فقط أنه يتعين علينا تربيعه. لاحظ أنه كان من الممكن أن نكتب nb**2 ، الأمر يتعلق بنفس الشيء.
  • for nb in liste_origine : هذا هو المكان الذي يأتي منه المتغير nb . نتعرف على بناء جملة الحلقة for ، إلا أننا لسنا معتادين على رؤيتها بهذا الشكل.
عندما تفسر Python هذا السطر ، فإنها ستتكرر خلال القائمة الأصلية وتربيع كل عنصر في القائمة. ثم تقوم بإرجاع النتيجة ، كقائمة بنفس طول القائمة الأصلية. يمكننا بطبيعة الحال التقاط هذه القائمة الجديدة في متغير.

التصفية مع التفريع الشرطي


يمكنك أيضًا تصفية قائمة مثل هذه:


>>> liste_origine = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> [nb for nb in liste_origine if nb % 2 == 0]
[2, 4, 6, 8, 10]
>>>  
يضاف شرط في نهاية التعليمات والذي سيحدد القيم التي سيتم نقلها إلى القائمة الجديدة. هنا ، يتم نقل القيم الزوجية فقط. في النهاية ، ننتهي بقائمة صغيرة بمقدار ضعف القائمة الأصلية.

دعونا نخلط كل ذلك


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

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


>>> qtt_a_retirer = 7 # نزيل 7 فواكه من كل نوع كل أسبوع
>>> fruits_stockes = [15, 3, 18, 21] # على سبيل المثال 15 تفاحة ، 3 شمام ...
>>> [nb_fruits-qtt_a_retirer for nb_fruits in fruits_stockes if nb_fruits>qtt_a_retirer]
[8, 11, 14]
>>>
 
كما ترون ، الكمية 3 فواكه لم تنجو خلال أسبوع التسوق هذا. بالطبع ، هذا المثال ليس كاملاً: لا توجد طريقة موثوقة لربط الأرقام المتبقية بالفواكه. لكن لديك مثال لتصفية وتعديل قائمة.

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

تطبيق جديد ملموس


مرة أخرى ، الأمر متروك لك.

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


>>> inventaire = [
...     ("pommes", 22),
...     ("melons", 4),
...     ("poires", 18),
...     ("fraises", 76),
...     ("prunes", 51),
... ]
>>>
 
انسخ هذه القائمة. تحتوي على مجموعات ، كل منها يحتوي على زوج: اسم الفاكهة وكميتها في المخزن.

مهمتك هي فرز هذه القائمة وفقًا لكمية كل فاكهة. بمعنى آخر ، يجب أن نحصل على شيء مشابه لـ:


 [
    ("fraises", 76),
    ("prunes", 51),
    ("pommes", 22),
    ("poires", 18),
    ("melons", 4),
]
 
بالنسبة لأولئك الذين لم يكن لديهم فضول للنظر في توثيق القوائم ، أوجه انتباهكم إلى طريقة sort فرز القائمة. يمكنك أيضًا استخدام الوظيفة sorted التي تأخذ القائمة للفرز كمعامل (هذه ليست طريقة قائمة ، كن حذرًا) . sortedيُرجع القائمة التي تم فرزها دون تعديل القائمة الأصلية ، والتي يمكن أن تكون مفيدة في بعض الظروف ، هذه القائمة بالتحديد. الأمر متروك لك ، يمكنك القيام بذلك بالطريقتين.

بالطبع ، حاول القيام بهذا التمرين باستخدام تفاهمات القائمة.

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

هذا هو التصحيح الذي أقترحه عليك:


# نغير معنى المخزون ، الكمية قبل الاسم
inventaire_inverse = [(qtt, nom_fruit) for nom_fruit,qtt in inventaire]
# علينا فقط فرز المخزون العكسي بترتيب تنازلي
# نقوم بإعادة تكوين المخزون المُرتب
inventaire = [(nom_fruit, qtt) for qtt,nom_fruit in sorted(inventaire_inverse, \
    reverse=True)]
 
إنه يعمل وتم العلاج في سطرين.

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


# نغير معنى المخزون ، الكمية قبل الاسم
inventaire_inverse = [(qtt, nom_fruit) for nom_fruit,qtt in inventaire]
# نقوم بفرز المخزون العكسي بترتيب تنازلي
inventaire_inverse.sort(reverse=True)
# ونعيد تشكيل المخزون
inventaire = [(nom_fruit, qtt) for qtt,nom_fruit in inventaire_inverse]
 
التجربة ، الممارسة ، ربما ستحتاج إليها ، بناء الجملة ليس بسيطًا جدًا في البداية. وتجنب الوقوع في أقصى الحدود أيضًا: بعض العمليات غير مجدية من خلال فهم القائمة أو أنها مكثفة للغاية بحيث لا يمكن فهمها بسهولة. في المثال السابق ، كان من الممكن جدًا استبدال سطرين إلى ثلاثة أسطر من التعليمات بخط واحد ، ولكن كان من الصعب قراءة ذلك. لا تضحي بقابلية القراءة من أجل تقصير الكود الخاص بك.

باختصار


  • يمكنك تقسيم سلسلة بناءً على فاصل باستخدام طريقة split السلسلة.
  • يمكنك الانضمام إلى قائمة تحتوي على سلاسل باستخدام طريقة السلسلة join . يجب استدعاء هذه الطريقة على الفاصل.
  • يمكنك إنشاء وظائف تنتظر عددًا غير معروف من المعلمات بفضل بناء الجملة def fonction_inconnue(*parametres) : (تم العثور على المعلمات التي تم تمريرها في معاملات المجموعة ) .
  • على قوائم تفاهمات تسمح لتصفح وتصفية تسلسل عن طريق إرسال واحدة جديدة.