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


الدرس: تحديد الخصائص


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

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

ما هو التغليف (encapsulation)؟



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

لكن هذا غبي! كيف يمكنني الوصول إلى السمات؟

وسوف نحدد أساليب محددة إلى حد ما، وتسمى   الموصّلون والمحوِّرات (accessors and mutators) . الموصِّلون يمنحون الوصول إلى السمة. تجعل التحولات من الممكن تعديلها. بشكل ملموس ، بدلاً من الكتابة mon_objet.mon_attribut ، ستكتب mon_objet.get_mon_attribut() . بنفس الطريقة ، لتعديل السمة الكتابة mon_objet.set_mon_attribut(valeur) وليس mon_objet.mon_attribut = valeur .

Get تعني "استعادة" ، وهي البادئة المستخدمة عمومًا للموصل.
set تعني ، في هذا السياق ، "التعديل" ؛ هذه هي البادئة المعتادة للطفرة.
كل شيء ملتوي! لماذا لا يمكننا الوصول إلى السمات مباشرة كما فعلنا في الفصل السابق؟
آه لكن أولاً ، لم أقل إنك لا تستطيع ذلك . يمكنك الوصول إلى سمات كائن ما مباشرة ، كما فعلنا في الفصل السابق. أقوم هنا بتلخيص مبدأ التغليف (encapsulation) فقط كما يمكن العثور عليه في لغات أخرى. في بايثون ، يكون الأمر أكثر دقة قليلاً.

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

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

لدى Python فلسفة مختلفة قليلاً: بالنسبة لجميع الكائنات التي لا نتوقع منها إجراءً معينًا ، سنصل إليها مباشرةً ، كما فعلنا في الفصل السابق. يمكنك الوصول إليها وتعديلها ببساطة عن طريق الكتابة mon_objet.mon_attribut . وبالنسبة للبعض ، سنقوم بإنشاء خصائص.

الخصائص



بادئ ذي بدء ، توضيح صغير: في C ++ أو Java على سبيل المثال ، في تعريف الفئة (كلاس) ، قمنا بإعداد مبادئ الوصول التي تشير إلى ما إذا كانت السمة (أو مجموعة السمات) خاصة أم عامة.(private or public) للتخطيط ، إذا كانت السمة عامة(public) ، فيمكن الوصول إليها من خارج الفئة (كلاس) وتعديلها. إذا كانت (private) ، فلا يمكننا ذلك. يجب أن نمر من خلال الموصّلات أو المحوّرات.

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

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

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

لكن ما هي هذه الخصائص؟

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

الخصائص عمليا



لا يتم إنشاء الخاصية في المُنشئ ولكن في متن الفئة (كلاس) . قلت أنه كان فئةً ، واسمها property . تتوقع أربع معلمات ، كلها اختيارية:

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

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


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

    
    def __init__(self, nom, prenom):
        """منشئ الفئة"""
        self.nom = nom
        self.prenom = prenom
        self.age = 33
        self._lieu_residence = "Paris" # لاحظ الشرطة السفلية _ أمام الاسم
    def _get_lieu_residence(self):
    """ الطريقة التي سيتم استدعاؤها عندما نريد الوصول في وضع القراءة
        إلى السمة "lieu_residence""""
        
        
        print("نصل إلى السمة lieu_residence!")
        return self._lieu_residence
    def _set_lieu_residence(self, nouvelle_residence):
        """ تسمى الطريقة عندما تريد تغيير مكان الاقامة """
        print("انتبه ، يبدو أن {} بصدد الانتقال إلى {}. ".format( \
                self.prenom, nouvelle_residence))
        self._lieu_residence = nouvelle_residence
    # سنخبر بايثون أن سمة أماكن الإقامة الخاصة بنا تشير إلى 
    # خاصية
    lieu_residence = property(_get_lieu_residence, _set_lieu_residence)
 
يجب عليك (نأمل) التعرف على البنية العامة للفئة. من ناحية أخرى ، على مستوى مكان الإقامة ، تتغير الأمور قليلاً:
  • بادئ ذي بدء ، في المنشئ ، لا ننشئ سمة self.lieu_residence ولكن self._lieu_residence . لا يوجد سوى حرف اختلاف صغير واحد ، وضع الشرطة السفلية _ في رأس اسم السمة. ومع ذلك ، فإن هذه العلامة تغير الكثير من الأشياء. الاصطلاح هو أننا لا نصل ، من خارج الفئة ، إلى سمة تبدأ بشرطة سفلية _ . إنها اتفاقية ، لا شيء يمنعك ... باستثناء الفطرة السليمة مرة أخرى.
  • نحدد الطريقة الأولى ، بدءًا أيضًا بشرطة سفلية _ ، مسماة _get_lieu_residence . إنها نفس القاعدة بالنسبة للسمات: لا يمكن الوصول ، من خارج الفئة (كلاس) ، إلى طريقة تبدأ بشرطة سفلية _ . إذا فهمت توضيحي الصغير حول الموصّلات والمحوِّرات ، فيجب أن تفهم بسرعة ما هي هذه الطريقة: إنها تُرجع فقط مكان الإقامة. مرة أخرى ، السمة التي تم التلاعب بها ليست lieu_residence ولكن _lieu_residence . نظرًا لأننا في الفئة ، لدينا الحق في التلاعب بها.
  • الطريقة الثانية تأخذ شكل المحوّر. إنه يسمي نفسه _set_lieu_residence وبالتالي يجب أن يتعذر الوصول إليه من خارج الفئة (كلاس) . على عكس الملحق ، فإنه يأخذ معلمة واحدة: مكان الإقامة الجديد. في الواقع ، إنها طريقة يجب استدعاؤها عندما يسعى المرء إلى تعديل مكان الإقامة ، وبالتالي فهو يحتاج إلى مكان الإقامة الجديد الذي يرغب في رؤيته مخصصًا للكائن.
  • أخيرًا ، السطر الأخير من الفئة مثير جدًا للاهتمام. هذا هو تعريف الخاصية. يُقال أن السمة lieu_residence (هذه المرة بدون شرطة سفلية _) يجب أن تكون خاصية. نحدد في ممتلكاتنا ، بالترتيب ، طريقة الوصول (الموصّل) وطريقة التعديل (المتحور).
عندما نريد الوصول objet.lieu_residence ، تصادف Python خاصية تعيد التوجيه إلى الطريقة _get_lieu_residence . عندما تريد تعديل قيمة السمة ، بالكتابة objet.lieu_residence = valeur، تستدعي Python الطريقة _set_lieu_residence بتمريرها القيمة الجديدة كمعامل.

انه غير واضح ؟ انظر هذا المثال:


>>> jean = Personne("Micado", "Jean")
>>> jean.nom
'Micado'
>>> jean.prenom
'Jean'
>>> jean.age
33
>>> jean.lieu_residence
نصل إلى السمة lieu_residence!
'Paris'
>>> jean.lieu_residence = "Berlin"
كن حذرا ، يبدو أن جين ينتقل إلى برلين.
>>> jean.lieu_residence
نصل إلى السمة lieu_residence!
'Berlin'
>>>
 
يعرض الموصّل والمحوّل الخاص بنا رسالة فقط ، حتى ندرك أنهما يتم استدعاؤهما عندما نريد معالجة السمة lieu_residence . يمكنك أيضًا تحديد موصّل واحد فقط ، وفي هذه الحالة لا يمكن تعديل السمة.

من الممكن أيضًا تحديد ، في الموضع الثالث للمنشئ property ، طريقة سيتم استدعاؤها عندما نكتب ذلك del objet.lieu_residence، وفي الموضع الرابع ، طريقة سيتم استدعاؤها عندما نكتب ذلك help(objet.lieu_residence) . يتم استخدام هاتين الميزتين الأخيرتين قليلاً ولكنهما موجودان.

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

دعونا نلخص مبدأ التغليف (encapsulation) في بايثون


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

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

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

باختصار


  • تُستخدم الخصائص للتحكم في الوصول إلى سمات معينة للمثيل.
  • والوارد تعريفها في الجسم من الطبقة مع بناء الجملة التالية: nom_propriete = property(methode_accesseur, methode_mutateur, methode_suppression, methode_aide) .
  • ثم يتم استدعاؤها عن طريق الكتابة objet.nom_propriete كما هو الحال بالنسبة لأي سمة.
  • إذا كنت ترغب فقط في قراءة السمة ، فسيتم استدعاء الطريقة المحددة على أنها موصّل.
  • إذا كنت ترغب في تعديل السمة ، فسيتم استدعاء طريقة المحوّر ، إذا تم تعريفها.
  • كل من المعلمات التي يتم تمريرها property اختيارية.