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


الدرس: TP: إنشاء قاموس منظم


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

مهمتنا



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

قد لا تتذكره ، لكني أخبرتك في الفصل الخاص بالقواميس أنه نوع غير مرتب. لذلك ، لا يهم الترتيب الذي تدخل به البيانات. لا يمكنك فرزها أو قلبها ، فلن يكون ذلك منطقيًا بالنسبة لهذا النوع بالذات.

لكن دعنا ننتهز الفرصة لإنشاء نموذج قاموس منظم. الفكرة ببساطة هي تخزين بياناتنا في قائمتين:

  • الأول يحتوي على مفاتيحنا ؛
  • الثانية تحتوي على القيم المقابلة.
سيكون ترتيب الإضافة مهمًا ، يمكننا فرز وعكس هذا النوع من القاموس.

المواصفات


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

  1. يجب أن نكون قادرين على إنشاء القاموس بعدة طرق:
    • فارغ: يتم استدعاء المُنشئ دون تمرير أي معلمات وبالتالي يكون القاموس الذي تم إنشاؤه فارغًا.
    • تم نسخه من قاموس: نقوم بتمرير قاموس كمعامل للمُنشئ ثم نقوم بنسخه إلى كائننا. وبالتالي من الممكن الكتابة constructeur(dictionnaire) ويتم نسخ المفاتيح والقيم الموجودة في القاموس إلى الكائن المركب.
    • مملوء مسبقًا باستخدام المفاتيح والقيم التي تم تمريرها كمعلمات: مثل القواميس المعتادة ، يجب أن يكون لدينا هنا إمكانية ملء كائننا مسبقًا بأزواج مفتاح القيمة التي تم تمريرها كمعامل ( constructeur(cle1 = valeur1, cle2 = valeur2, …)) .
  2. يجب إقران المفاتيح والقيم. بمعنى آخر ، إذا أردنا حذف مفتاح ، فيجب أيضًا حذف القيمة المقابلة. عندما تكون المفاتيح والقيم في قوائم من نفس الحجم ، يكفي أخذ الفهرس في قائمة واحدة لمعرفة الكائن الذي يتوافق معها في القائمة الأخرى. على سبيل المثال ، 0 يقترن مفتاح الفهرس بقيمة الفهرس 0 .
  3. يجب أن نكون قادرين على التفاعل مع كائن الحاوية الخاص بنا باستخدام أقواس مربعة ، لاسترداد قيمة ( objet[cle]) ، لتعديلها ( objet[cle] = valeur) أو حذفها ( del objet[cle]) .
  4. عندما نحاول تعديل قيمة ، إذا كان المفتاح موجودًا ، نكتب القيمة القديمة ، وإذا لم يكن موجودًا ، نضيف زوج المفتاح والقيمة في نهاية القاموس.
  5. يجب أن نكون قادرين على استعمال in لمعرفة ما  إذا كان المفتاح موجودًا في قاموسنا ( cle in dictionnaire )  بفضل الكلمة الأساسية .
  6. يجب أن نكون قادرين على طلب حجم القاموس بفضل الوظيفة len .
  7. يجب أن نكون قادرين على عرض قاموسنا مباشرة في المترجم أو باستخدام الوظيفة print . يجب أن يكون العرض مماثلاً لعرض القواميس المعتادة ( {cle1: valeur1, cle2: valeur2, …}) .
  8. يجب أن يحدد الكائن طرق sort فرزها reverse وعكسها. يجب أن يتم فرز الكائن وفقًا للمفاتيح.
  9. يجب أن نكون قادرين على المرور في الكائن. عندما تكتب for cle in dictionnaire، عليك تصفح قائمة المفاتيح الموجودة في القاموس.
  10. مثل القواميس ، يجب تنفيذ ثلاث طرق  keys() ( إرجاع قائمة المفاتيح )، values() (إعادة قائمة القيم) و items() (إعادة الأزواج (مفتاح ، قيمة)) . يُترك نوع إرجاع هذه الأساليب لمبادرتك: يمكن أن تكون مكررات أو مولدات (طالما يمكننا المرور بها).
  11. يجب أن نكون قادرين على إضافة قواميسين مرتبتين ( dico1 + dico2) ؛ تتم إضافة مفاتيح وقيم القاموس الثاني إلى الأول.
هذا يجعلك تعمل!

ولا يزال بإمكانك إيجاد طريقة لتحسين صفك بعد ذلك ، إذا كنت ترغب في ذلك.

مثال على المعالجة


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


>>> fruits = DictionnaireOrdonne()
>>> fruits
{}
>>> fruits["pomme"] = 52
>>> fruits["poire"] = 34
>>> fruits["prune"] = 128
>>> fruits["melon"] = 15
>>> fruits
{'pomme': 52, 'poire': 34, 'prune': 128, 'melon': 15}
>>> fruits.sort()
>>> print(fruits)
{'melon': 15, 'poire': 34, 'pomme': 52, 'prune': 128}
>>> legumes = DictionnaireOrdonne(carotte = 26, haricot = 48)
>>> print(legumes)
{'carotte': 26, 'haricot': 48}
>>> len(legumes)
2
>>> legumes.reverse()
>>> fruits = fruits + legumes
>>> fruits
{'melon': 15, 'poire': 34, 'pomme': 52, 'prune': 128, 'haricot': 48, 'carotte':
26}
>>> del fruits['haricot']
>>> 'haricot' in fruits
False
>>> legumes['haricot']
48
>>> for cle in legumes:
...     print(cle)
...
haricot
carotte
>>> legumes.keys()
['haricot', 'carotte']
>>> legumes.values()
[48, 26]
>>> for nom, qtt in legumes.items():
...     print("{0} ({1})".format(nom, qtt))
...
haricot (48)
carotte (26)
>>>
 

كل شيء في البداية!


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

انطلاق !

التصحيح المقترح



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


class DictionnaireOrdonne:
    """ قاموسنا المرتب. يتم الحفاظ على ترتيب البيانات
     وبالتالي ، على عكس القواميس المعتادة ، يمكن فرزها
     أو مشاهدة ترتيب بياناتها معكوسة """
    
    def __init__(self, base={}, **donnees):
        """ منشئ كائننا. قد لا يستغرق الأمر أي معلمة
         (في هذه الحالة ، سيكون القاموس فارغًا) أو أنشئ ملف
         القاموس مليء بـ:
         - إلى القاموس "الأساسي" الذي تم تمريره كمعامل أول ؛
         - إلى القيم الموجودة في "البيانات". """
        
        self._cles = [] # قائمة تحتوي على مفاتيحنا
        self._valeurs = [] # قائمة تحتوي على القيم المقابلة لمفاتيحنا
        
       # قاموس قابل للاستغلال "base"   نتحقق من أن 
        if type(base) not in (dict, DictionnaireOrdonne):
            raise TypeError( \
                " النوع المتوقع هو قاموس (عادي أو مرتب)")
        
        # 'base' نحصل على البيانات من 
        for cle in base:
            self[cle] = base[cle]
        
        # 'donnees' نحصل على البيانات من
        for cle in donnees:
            self[cle] = donnees[cle]
    
    def __repr__(self):
        """ تمثيل كائننا. هذه هي القناة التي سيتم عرضها
         عند إدخال القاموس مباشرة في المترجم الفوري ، أو بواسطة
         باستخدام وظيفة "repr""""
        
        chaine = "{"
        premier_passage = True
        for cle, valeur in self.items():
            if not premier_passage:
                chaine += ", " # On ajoute la virgule comme séparateur
            else:
                premier_passage = False
            chaine += repr(cle) + ": " + repr(valeur)
        chaine += "}"
        return chaine
    
    def __str__(self):
        """ يتم استدعاء الوظيفة عندما تريد عرض القاموس باستخدام
           أو تحويلها إلى سلسلة باستخدام المنشئ print  الوظيفة
         ". __repr__" نعيد التوجيه إلىstr """
        
        return repr(self)
    
    def __len__(self):
        """ إرجاع حجم القاموس """
        return len(self._cles)
    
    def __contains__(self, cle):
        """ ترجع صواب إذا كان المفتاح موجودًا في قائمة المفاتيح ، وخطأ بخلاف ذلك """
        return cle in self._cles
    
    def __getitem__(self, cle):
        """يُعيد القيمة المناسبة للمفتاح اذا كان موجودا
عكس ذلك          KeyError و يُعيد خطأ """
        
        if cle not in self._cles:
            raise KeyError( \
                " المفتاح {0} غير موجود في القاموس ".format( \
                cle))
        else:
            indice = self._cles.index(cle)
            return self._valeurs[indice]
    
    def __setitem__(self, cle, valeur):
        """ استدعاء طريقة خاصة عند محاولة تعديل مفتاح
         موجودة في القاموس. إذا كان المفتاح غير موجود ، نضيفه
         في نهاية القاموس """
        
        if cle in self._cles:
            indice = self._cles.index(cle)
            self._valeurs[indice] = valeur
        else:
            self._cles.append(cle)
            self._valeurs.append(valeur)
    
    def __delitem__(self, cle):
        """ تسمي الطريقة عندما تريد حذف مفتاح """
        if cle not in self._cles:
            raise KeyError( \
                " المفتاح {0} غير موجود في القاموس ".format( \
                cle))
        else:
            indice = self._cles.index(cle)
            del self._cles[indice]
            del self._valeurs[indice]
    
    def __iter__(self):
        """ طريقة اجتياز الكائن. نعيد مكرر المفتاح """
        return iter(self._cles)
    
    def __add__(self, autre_objet):
        """ نعيد قاموسًا جديدًا يحتوي على الاثنين
         قواميس شاملة (أول ذاتي ثم كائن_آخر)"""
        
        if type(autre_objet) is type(self):
            raise TypeError( \
                " لا يمكن ربط {0} و {1}".format( \
                type(self), type(autre_objet)))
        else:
            nouveau = DictionnaireOrdonne()
            
            # نبدأ بنسخ الذات في القاموس
            for cle, valeur in self.items():
                nouveau[cle] = valeur
            
            # ثم نقوم بنسخ other_object
            for cle, valeur in autre_objet.items():
                nouveau[cle] = valeur
            return nouveau
    
    def items(self):
        """ إرجاع مولد يحتوي على أزواج (مفتاح ، قيمة)"""
        for i, cle in enumerate(self._cles):
            valeur = self._valeurs[i]
            yield (cle, valeur)
    
    def keys(self):
        """ تقوم هذه الطريقة بإرجاع قائمة المفاتيح """
        return list(self._cles)
    
    def values(self):
        """ ترجع هذه الطريقة قائمة القيم """
        return list(self._valeurs)
    
    def reverse(self):
        """ انعكاس القاموس """
        # نقوم بإنشاء قائمتين فارغتين تحتويان على الترتيب الجديد للمفاتيح
        # والقيم
        cles = []
        valeurs = []
        for cle, valeur in self.items():
            # نضيف المفاتيح والقيم في بداية القائمة
            cles.insert(0, cle)
            valeurs.insert(0, valeur)
        # ثم نقوم بتحديث قوائمنا
        self._cles = cles
        self._valeurs = valeurs
    
    def sort(self):
        """ طريقة فرز القاموس حسب مفاتيحه """
        # نقوم بفرز المفاتيح
        cles_triees = sorted(self._cles)
        # نقوم بإنشاء قائمة القيم ، لا تزال فارغة
        valeurs = []
        # ثم ننتقل إلى قائمة المفاتيح المصنفة
        for cle in cles_triees:
            valeur = self[cle]
            valeurs.append(valeur)
        # أخيرًا ، نقوم بتحديث قائمة المفاتيح والقيم الخاصة بنا
        self._cles = cles_triees
        self._valeurs = valeurs
 

الكلمة الأخيرة


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

لا تتردد في تحسين منتجنا ، سيكون أجمل وأكثر فائدة مع ميزات إضافية!

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