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


الدرس: افهم الفئات (Class)


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

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

الفئات العالم لوحدها



في القسم السابق ، وصفت بإيجاز الكائنات (object) على أنها متغيرات يمكن أن تحتوي بدورها على وظائف ومتغيرات. لقد ذهبنا إلى أبعد من ذلك في الجزء 2 ، لنجد أن "الوظائف الموجودة في كائناتنا (object) " تسمى عمليات. في الحقيقة ، اقتصرت على التعريف "العملي" للأشياء ، بينما تخفي وراء OOP (البرمجة الشيئية) فلسفة حقيقية.

لماذا نستخدم الأشياء(object) ؟


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

تعرف Java ، اللغة التي ظهرت في نفس الوقت تقريبًا مع Python ، فلسفة مختلفة تمامًا عن فلسفة C ++ : على عكس الأخيرة ، تتطلب Java أن يتم وضع كل شيء في فئات. حتى التطبيق الاعتيادي Hello World موجود في فئة (كلاس) .

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

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

حتى الآن ، رأينا فقط جانبًا تقنيًا للكائن (object ) . سأذهب إلى حد القول إن ما رأيناه حتى الآن كان مجرد طريقة "أكثر جمالية قليلاً" للبرمجة: إنه أسهل وأكثر قابلية للفهم ma_liste.append(5) من الكتابة append_to_list(ma_liste, 5) . لكن خلف OOP ، ليس هناك اهتمام جمالي فقط ، بعيدًا عن ذلك.

اختيار النموذج


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

لكن ما الذي سنقوم بتصميمه؟ يكون توجيه الكائن (object) أكثر من مفيد عندما يتم استخدامه للنمذجة أو تمثيل البيانات أكثر تعقيدًا من رقم بسيط أو سلسلة من الأحرف. بالطبع ، هناك فئات تحددها Python لنا: الأرقام والسلاسل والقوائم هي واحدة منها. لكننا سنكون محدودين للغاية إذا لم نتمكن من القيام فِئَاتنا الخاصة.

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

اصطلاح التسمية


بعيدًا عني فكرة تعقيد التمرين ولكن إذا أشرنا إلى PEP 8 من Python ، فمن الأفضل استخدام أسماء الأصناف المعروفة باسم Camel Case  .

إن PEPs هي " Python Enhancement Proposals" ، أي مقترحات تحسين Python .

لا يستخدم هذا الاصطلاح الشرطة السفلية _ لفصل الكلمات. الفكرة هي كتابة كل حرف بأحرف كبيرة بداية كلمة ، على سبيل المثال MaClasse .

لذا فإن هذا هو العرف الذي سأستخدمه لأسماء الفئات. أنت حر في تغييره ، مرة أخرى لا شيء مفروض.

لتحديد فئة (كلاس) جديدة ، نستخدم الكلمة الأساسية class .

بناء الجملة هي بديهية إلى حد ما:  class NomDeLaClasse: .

لا تقم بتشغيل هذا الكود الآن ، فنحن لا نعرف كيفية تعيين سماتنا وطرقنا.

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

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

لتحديد سمات كائننا (object) ، يجب علينا تحديد مُنشئ في صنفنا. دعونا نلقي نظرة فاحصة.

صفاتنا الأولى



حددنا السمات التي من شأنها أن تميز كائن (object) فئة (كلاس) لدينا Personne . الآن علينا أن نحدد في صنفنا طريقة خاصة ، تسمى المُنشئ (constructor) ، والتي تُستدعى دائمًا عندما نريد إنشاء كائن (object) من صنفنا.

بشكل ملموس ، المُنشئ هو أسلوب كائننا (object) المسؤول عن إنشاء سماتنا. في الحقيقة ، إنها الطريقة التي سيتم استدعاؤها عندما نريد إنشاء كائننا (object).

دعنا نرى الكود ، سيكون أكثر فائدة:


class Personne: # تعريف فئة (كلاس)  الشخص لدينا
    """ فئة (كلاس)  تحدد الشخص وتتميز بـ:
    - اسمه
    - إسمه الاول
    - عمره
    - مكان اقامته"""

    
    def __init__(self): # طريقة المنشئ لدينا
        """ في الوقت الحالي ، سنحدد سمة واحدة فقط """
        self.nom = "Dupont"
 
دعونا نرى بالتفصيل:

  • أولا ، تعريف الفئة (كلاس) . يتكون من الكلمة الأساسية class واسم الفئة (كلاس) ونقطتي الطقوس ":" .
  • Docstring تعليق واحد على الفئة (كلاس) . مرة أخرى ، هذه عادة رائعة يجب أن تتخذها وأنا أشجعك على القيام بها باستمرار. يمكن أن يكون هذا أكثر من مفيد عندما تشرع في مشاريع كبيرة ، خاصة مع العديد منها.
  • تعريف منشئنا. كما ترون ، هذا تعريف تقريبًا "كلاسيكي" للدالة. اسمها __init__ثابت: في Python ، تسمى جميع المنشئات بهذا الاسم. سنرى لاحقًا أن أسماء العمليات المحاطة على كلا الجانبين بعلامتين تحتها خط (__nommethode__)هي طرق خاصة . لاحظ أنه في تعريف طريقتنا ، نقوم بتمرير معلمة مسماة أولاً self .
  • واحد جديد docstring . أنا لا أقوم بتعقيد دون داع ، لذلك أحدد أننا سنقوم ببساطة بتعريف سمة واحدة في الوقت الحالي في المُنشئ الخاص بنا.
  • في المُنشئ الخاص بنا ، نجد تماثل السمة الخاصة بنا nom . ننشئ متغيرًا self.nom ونعطيه قيمة Dupont . سأفصل ما يجري هنا قليلاً في الأسفل.
بادئ ذي بدء ، لرؤية النتيجة قيد التنفيذ ، دعنا نحاول إنشاء كائن (object) من الفئة (كلاس) لدينا:


>>> bernard = Personne()
>>> bernard
<__main__.Personne object at 0x00B42570>
>>> bernard.nom
'Dupont'
>>>
 
عندما نطلب من المترجم الفوري عرض كائننا (object) مباشرة bernard ، يظهر شيء غير صالح للقراءة قليلاً ... حسنًا ، الشيء الرئيسي هو الإشارة التي تحدد الفئة (كلاس) التي يأتي منها الكائن (object) . لذلك يمكننا التحقق من أن الفئة (كلاس) هي بالفعل Personne الذي يأتي منه هدفنا. نحاول بعد ذلك عرض سمة nom من كائننا (object) bernard ونحصل على 'Dupont' (القيمة المحددة في المنشئ الخاص بنا) . لاحظ أننا نستخدم النقطة (.) ، التي تُستخدم مرارًا وتكرارًا لعلاقة العضوية (  nom وهي سمة من سمات الكائن (object)  bernard ) . بعض التفسيرات الأخرى:

عندما نصنع كائن (object) ا ...


عندما نكتب Personne()، نستدعي مُنشئ  فئتنا Personne بطريقة غير مباشرة إلى حد ما لن أفصلها هنا. فإنه يأخذ معلمة متغير غامضة قليلا:  self . في الواقع ، إنه ببساطة كائن (object) نا في طور الخلق. نكتب هذا في سمة الكائن (object)  nom  self.nom = "Dupont" . في النهاية استدعاء المُنشئ ، تُعيد Python الكائن (object)  self المُعدَّل ، جنبًا إلى جنب مع السمة الخاصة بنا. سوف نتلقى كل شيء في المتغير الخاص بنا bernard .

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

دعونا نوسع منشئنا قليلا


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

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

هذا هو الكود ، فقط في حالة:


class Personne:
    """ فئة (كلاس)  تحدد الشخص وتتميز بـ:
    - اسمه
    - اسمه الاول
    - عمره
    - مكان اقامته"""

    
    def __init__(self): # طريقة المنشئ لدينا
        """ منشئ صفنا. سيتم إنشاء مثيل لكل سمة
        بقيمة افتراضية ... أصلي """

        
        self.nom = "Dupont"
        self.prenom = "Jean" 
        self.age = 33 
        self.lieu_residence = "Paris"
 
هل هذا يبدو واضحا لك؟ كود مثال صغير آخر:


>>> jean = Personne()
>>> jean.nom
'Dupont'
>>> jean.prenom
'Jean'
>>> jean.age
33
>>> jean.lieu_residence
'Paris'
>>> # Jean déménage…
... jean.lieu_residence = "Berlin"
>>> jean.lieu_residence
'Berlin'
>>>
 
يبدو هذا المثال واضحًا إلى حد ما بالنسبة لي ، على مبدأ تعريف السمات ، والوصول إلى سمات كائن (object) تم إنشاؤه ، وتعديل سمات الكائن object) ).

شرح صغير جدًا بخصوص السطر 11: في العديد من البرامج التعليمية ، لا نوصي بتعديل سمة مثيل (سمة لكائن (object) ) كما فعلنا للتو ، بمجرد القيام بذلك objet.attribut = valeur . إذا أتيت من لغة أخرى ، فربما تكون قد سمعت عن الموصلين والمحورين(accessors and mutators). تتكرر هذه المفاهيم في بعض دروس بايثون ، لكن لا يجب أن تكون بهذه اللغة بالضبط. كل هذا سأفصله في الفصل التالي. في الوقت الحالي ، تحتاج فقط إلى معرفة أنه عندما تريد تغيير سمة كائن (object) ، فأنت تكتب object.attribut = new_value . سنرى الحالات المحددة لاحقًا.

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


class Personne:
    """ فئة (كلاس)  تحدد الشخص الذي يتميز به:
    - اسمه
    - اسمه الاول
    - عمره
    - مكان اقامته"""

    
    def __init__(self, nom, prenom):
        """منشئ الفئة (كلاس)  (كلاس) """
        self.nom = nom
        self.prenom = prenom
        self.age = 33
        self.lieu_residence = "Paris"
 
وفي الصور:


>>> bernard = Personne("Micado", "Bernard")
>>> bernard.nom
'Micado'
>>> bernard.prenom
'Bernard'
>>> bernard.age
33
>>>
 
تذكر أن المعلمة الأولى يجب أن تكون  self.  بصرف النظر عن ذلك ، يعد المنشئ وظيفة كلاسيكية جدًا: يمكنك تحديد المعلمات ، افتراضيًا أم لا ، مسماة أم لا. عندما تريد إنشاء الكائن (object) الخاص بك ، ستقوم باستدعاء اسم الفئة (كلاس) عن طريق تمرير المعلمات لاستخدامها بين قوسين. قم بإجراء بعض الاختبارات ، باستخدام معلمات أكثر أو أقل ، أعتقد أنك ستدرك المبدأ بسرعة كبيرة.

سمات الفئة (كلاس) (كلاس)


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


class Compteur:
    """ تحتوي هذه الفئة (كلاس) على سمة فئة (كلاس)  تتزايد في كل مرة
    بمجرد إنشاء كائن  (object)  من هذا النوع """

    
    objets_crees = 0 # العداد هو 0 في البداية
    def __init__(self):
        """ في كل مرة نقوم فيها بإنشاء كائن  (object)  ، نقوم بزيادة العداد """
        Compteur.objets_crees += 1
 
نحدد سمة class الخاصة بنا مباشرة في جسم الفئة (كلاس) ، تحت التعريف و docstring، قبل تعريف المنشئ. عندما نريد أن نسميها في المنشئ ، فإننا نبدأ اسم صفة الفئة (كلاس) باسم الفئة (كلاس) (كلاس) . ويتم الوصول إليها بهذه الطريقة أيضًا ، خارج الفئة (كلاس) (كلاس) . انظر بدلاً من ذلك:


>>> Compteur.objets_crees
0
>>> a = Compteur() # نقوم بإنشاء أول كائن  (object) 
>>> Compteur.objets_crees
1
>>> b = Compteur()
>>> Compteur.objets_crees
2
>>>
 
في كل مرة نقوم فيها بإنشاء كائن (object) من النوع Compteur ، تتم  objets_crees زيادة سمة class بمقدار 1. قد يكون من المفيد أن يكون لديك سمات فئة (كلاس) ، عندما يجب أن تحتوي جميع الكائنات (object) لدينا على بعض البيانات المتطابقة. ستتاح لنا الفرصة للتحدث عنها لاحقًا.

الطرق ، الوصفة



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

لإنشاء طُرقنا الأولى ، سنقوم بنمذجة… مصفوفة. سبورة ، نعم هذا جيد.

سيكون لجدولنا سطح (سمة) يمكننا الكتابة عليه ، ويمكننا قراءته ومحوه. لإنشاء صفنا TableauNoir وسمتنا surface، يجب ألا تواجه مشكلة:


class TableauNoir:
    """ فئة (كلاس)  تحدد السطح الذي يمكننا الكتابة عليه ،
    يمكن قراءتها وحذفها ، عن طريق مجموعة من الأساليب. السمة المعدلة
    'surface' هو (سطح)"""

    
    def __init__(self):
        """ بشكل افتراضي ، سطحنا فارغ """
        self.surface = ""
 
لقد أنشأنا بالفعل طريقة ، لذلك لا ينبغي أن تفاجأ كثيرًا بالصيغة التي سنراها. المُنشئ الخاص بنا هو بالفعل طريقة ، فهو يحافظ على بناء الجملة. لذلك سنكتب طريقتنا  ecrire للبدء.


class TableauNoir:
    """ فئة (كلاس)  تحدد السطح الذي يمكننا الكتابة عليه ،
    يمكن قراءتها وحذفها ، عن طريق مجموعة من الأساليب. السمة المعدلة
    'surface' هو (سطح)"""

    
    def __init__(self):
        """ بشكل افتراضي ، سطحنا فارغ """
        self.surface = ""
    def ecrire(self, message_a_ecrire):
        """طريقة الكتابة على سطح السبورة.
        إذا لم يكن السطح فارغًا ، نتخطى سطرًا قبل الإضافة
        الرسالة المراد كتابتها """

        
        if self.surface != "":
            self.surface += "\n"
        self.surface += message_a_ecrire
 
دعنا ننتقل إلى الاختبار:


>>> tab = TableauNoir()
>>> tab.surface
''
>>> tab.ecrire("Coooool ! انها العطلة !")
>>> tab.surface
"Coooool ! انها العطلة !"
>>> tab.ecrire("عيدكم سعيد!")
>>> tab.surface
"Coooool ! انها العطلةs !\nعيدكم سعيد !"
>>> print(tab.surface)
Coooool ! انها العطلة !
عيدكم سعيد !
>>>
 
طريقتنا  ecrire تعتني بالكتابة على السطح لدينا، إضافة خط فاصل لفصل كل رسالة.

هنا نجد معلمتنا  self . حان الوقت لإلقاء نظرة فاحصة على الغرض منه.

المعلمة self


في عمليات المثيل(instance) لدينا ، والتي تسمى أيضًا طُرق الكائن (object)  ، نجد هذه المعلمة في التعريف self . حان الوقت لفهم ما يعنيه ذلك.

شيء واحد مهم: عند إنشاء كائن (object) جديد ، هنا السبورة ، تكون سمات الكائن (object) خاصة بالكائن (object) الذي تم إنشاؤه. هذا منطقي: إذا قمت بإنشاء العديد من السبورات ، فلن يكون لها نفس السطح. لذلك يتم تضمين السمات في الكائن (object) .

بدلاً من ذلك ، يتم تضمين الأساليب في الفئة (كلاس) التي تحدد كائن (object) نا. انه مهم جدا. عندما تكتب tab.ecrire(…)، ستبحث Python عن الطريقة ecrire ليس في الكائن (object)  tab، ولكن في الفصل TableauNoir .


>>> tab.ecrire
>
>>> TableauNoir.ecrire

>>> help(TableauNoir.ecrire)
Help on function ecrire in module __main__:
ecrire(self, message_a_ecrire)
طريقة الكتابة على سطح السبورة    .
     إذا لم يكن السطح فارغًا ، نتخطى سطرًا قبل الإضافة
     الرسالة المراد كتابتها.
>>> TableauNoir.ecrire(tab, "essai")
>>> tab.surface
'essai'
>>>
 
كما ترون ، عندما تكتبها tab.ecrire(…) فهي نفسها عندما تكتب TableauNoir.ecrire(tab, …) . المعلمة الخاصة بك self هي الكائن (object) الذي يستدعي الطريقة. لهذا السبب تقوم بتغيير سطح الكائن (object) عن طريق الاتصال self.surface .

باختصار ، عندما تحتاج إلى العمل في طريقة كائن (object) على الكائن (object) نفسه ، فإنك ستمر self .

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

أليس في الواقع وقت طويل أن تضطر دائمًا إلى العمل مع self كل مرة تريد فيها استخدام الكائن (object) ؟

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

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


class TableauNoir:
    """ فئة (كلاس)  تحدد السطح الذي يمكننا الكتابة عليه ،
    كن قراءتها وحذفها ، عن طريق مجموعة من الأساليب. السمة المعدلة
    هي 'surface'"""

    
    def __init__(self):
        """ بشكل افتراضي ، سطحنا فارغ """
        self.surface = ""
    def ecrire(self, message_a_ecrire):
        """ طريقة الكتابة على سطح السبورة.
        إذا لم يكن السطح فارغًا ، نتخطى سطرًا قبل الإضافة
        الرسالة المراد كتابتها """

        
        if self.surface != "":
            self.surface += "\n"
        self.surface += message_a_ecrire
    def lire(self):
        """ هذه الطريقة مسؤولة عن عرض ، بفضل الطباعة ،
        سطح السبورة"""

        
        print(self.surface)
    def effacer(self):
        """ هذه الطريقة تمحو سطح السبورة """
        self.surface = ""
 
ومرة أخرى ، كود الاختبار:


>>> tab = TableauNoir()
>>> tab.lire()
>>> tab.ecrire("Salut tout le monde.")
>>> tab.ecrire("La forme ?")
>>> tab.lire()
Salut tout le monde.
La forme ?
>>> tab.effacer()
>>> tab.lire()
>>>
 
وها أنت ذا! من خلال أساليبنا الموثقة جيدًا ، help(TableauNoir) ستحصل على القليل من الوصف الجيد لمدى فائدة فصلك. إنها مريحة للغاية ، لا تنسى السلاسل.

طرق الفئة (كلاس) والطرق الثابتة


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

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


class Compteur:
    """ تحتوي هذه الفئة (كلاس) على سمة فئة (كلاس)  تتزايد في كل مرة
    بمجرد إنشاء كائن  من هذا النوع """

    
    objets_crees = 0 # العداد هو 0 في البداية
    def __init__(self):
        """ في كل مرة نقوم فيها بإنشاء كائن  ، نقوم بزيادة العداد """
        Compteur.objets_crees += 1
    def combien(cls):
        """ طريقة فئة (كلاس)  تعرض عدد الكائنات  التي تم إنشاؤها """
        print("حتى الآن تم إنشاء {} من الكائنات  .".format(
                cls.objets_crees))
    combien = classmethod(combien)
 
دعونا نرى النتيجة أولاً:


>>> Compteur.combien()
تم إنشاء 0 كائن  حتى الآن.
>>> a = Compteur()
>>> Compteur.combien()
تم إنشاء 1 كائن  حتى الآن.
>>> b = Compteur()
>>> Compteur.combien()
تم إنشاء 2 كائن  حتى الآن.
>>>
 
لا تأخذ طريقة الفئة (كلاس) self إلا كمعاملها الأول cls . تحتوي هذه المعلمة على الفئة (كلاس) (هنا Compteur ).

لاحظ أنه يمكنك استدعاء طريقة الفئة (كلاس) من كائن (object) تم إنشاء مثيل له في الفئة (كلاس) (كلاس) . على سبيل المثال ، كان من الممكن أن تكتب a.combien() .

أخيرًا ، لكي تتعرف Python على طريقة الفئة (كلاس) ، يجب عليك استدعاء الوظيفة classmethod التي تأخذ الطريقة التي تريد تحويلها كمعامل وتعيد الطريقة المحولة.

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

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

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


class Test:
    """ مجرد فئة (كلاس)  للاختبار """
    def afficher():
        """ الوظيفة المسؤولة عن عرض شيء ما """
        print("نعرض نفس الشيء.")
        print("بغض النظر عن بيانات الكائن  أو الفئة (كلاس)  .")
    afficher = staticmethod(afficher)
 
إذا كنت مرتبكًا قليلاً مع سمات وطرق الفئة (كلاس) ، فلا بأس بذلك. تذكر على وجه الخصوص السمات وطرق المثال ، فهذه هي التي بقيت فيها بشكل أساسي وهي تلك التي ستجدها في معظم الأوقات.

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

القليل من التأمل



المزيد من الفلسفة؟

حسنًا ... أعترف أن مصطلح الاستبطان (introspection)  يبدو وكأنه شيء مجرد إلى حد ما. ومع ذلك ، ستفهم بسرعة الفكرة الكامنة وراء ذلك: تقدم Python العديد من التقنيات لاستكشاف كائن (object) ، ومعرفة أساليبها أو سماتها.

ماهي النقطة ؟ عندما تقوم بتطوير فئة (كلاس) ، فأنت تعرف عادة ما تحتويه ، أليس كذلك؟

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

الوظيفة dir


تقنية الاستبطان الأولى التي سنراها هي الوظيفة dir . يأخذ كائن (object) ا كمعامل ويعيد قائمة سماته وطرقه.


class Test:
    """ مجرد فئة (كلاس)  للاختبار """
    def __init__(self):
        """ نحدد سمة فريدة في المنشئ """
        self.mon_attribut = "ok"
    
    def afficher_attribut(self):
        """ طريقة عرض السمة 'my_attribute'"""
        print("Mon attribut est {0}.".format(self.mon_attribut))
>>> # لنقم بإنشاء كائن  (object)  من فئة (كلاس)  الاختبار
... un_test = Test()
>>> un_test.afficher_attribut()
Mon attribut est ok.
>>> dir(un_test)
['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__g
e__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '_
_setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'affich
er_attribut', 'mon_attribut']
>>>
 
تقوم الدالة dir بإرجاع قائمة تتضمن اسم سمات وأساليب الكائن (object) الذي تم تمريره إليها كمعامل. يمكنك أن تلاحظ أن كل شيء مختلط ، وهذا أمر طبيعي: بالنسبة لبايثون ، فإن الأساليب والوظائف والفئات والوحدات النمطية هي كائنات (object). أول ما يميز المتغير عن الوظيفة هو أن الوظيفة قابلة للتنفيذ (قابلة للاستدعاء ) . تقوم الوظيفة dir بإرجاع كل ما هو موجود في الكائن (object) دون تمييز.

أه ما كل هذا؟ لم نحدد مطلقًا كل هذه الأساليب أو السمات!

لا حقا. سنرى لاحقًا أن هذه طرق خاصة مفيدة لبايثون.

السمة الخاصة__dict__


بشكل افتراضي ، عندما تقوم بتوسيع فئة (كلاس) ، فإن جميع الكائنات (object) التي تم إنشاؤها من هذه الفئة (كلاس) سيكون لها سمة خاصة __dict__ . هذه السمة عبارة عن قاموس يحتوي ، كمفاتيح ، على أسماء السمات ، وقيم السمات ، كقيم.

انظر بدلاً من ذلك:


>>> un_test = Test()
>>> un_test.__dict__
{'mon_attribut': 'ok'}
>>>
 
لماذا "السمة الخاصة"؟

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

هل يمكننا تعديل هذا القاموس؟

تستطيع. لاحظ أنه بتغيير قيمة السمة ، يمكنك أيضًا تغيير السمة في الكائن (object) .


>>> un_test.__dict__["mon_attribut"] = "plus ok"
>>> un_test.afficher_attribut()
Mon attribut est plus ok.
>>>
 
بشكل عام ، استخدم الاستبطان فقط إذا كان لديك سبب وجيه للقيام بذلك وتجنب هذا النوع من بناء الجملة. لا يزال من الأنظف كتابة  object.attribute = value ذلك objet.__dict__[name_attribute] = value .

لن نذهب أبعد من ذلك في هذا الدرس. أعتقد أنك ستكتشف في بقية هذا الكتاب فائدة الطريقتين اللتين عرضتهما عليك.

باختصار



  • نحدد فئة (كلاس) باتباع بناء الجملة class NomClasse: .
  • يتم تعريف الطرق على أنها وظائف ، باستثناء أنها موجودة في جسم الفئة (كلاس) (كلاس) .
  • تأخذ طرق المثيل self مثيل الكائن (object) المستخدم كمعامل أول .
  • نقوم ببناء مثيل لفئة (كلاس) عن طريق استدعاء مُنشئها ، وهو أسلوب مثيل يسمى __init__ .
  • وهو يحدد سمات مثيل في منشئ فئة (كلاس) ، مع بناء الجملة التالية:  self.name_attribute = value