ملتقى الواحــة

نسخة كاملة : properties in Python
أنت حالياً تتصفح نسخة خفيفة من المنتدى . مشاهدة نسخة كاملة مع جميع الأشكال الجمالية .
properties in Python
عندما تحدثنا في موضوع سابق عن السمات، رأينا أنه يمكننا الوصول اليها بدون حماية. هذا الامر يثير استغراب القادمين من عالم السي++ او الجافا، فهم يعتبرون أن الوصول المباشر الى السمات يكسر مفهوم التغليف (encapsulation) في البرمجة الكائنية: تصور لو أن للصبورة التي أنشأناها سابقا مقاييس سلبية؟؟ هل يجوز هذا؟؟ وأين سنكتب اذا؟؟
كود :
class Board:
   def __init__(self, height=0, width=0):
       self.height = height
       self.width = width
       self.surface = ""
كود :
bb = Board(-20, 50)
bb.height               # -20
bb.width = -40
هذا المثال يبرز الحاجة الى حماية السمات من الاستخدام الخاطئ. بالنسبة لمبرمجي السي++ او الجافا، تلك السمات تكون اصلا محفوظة في مجال تسمية محمي ولا سبيل الى الوصول اليها او تعديلها الا عبر وظائف تسمى getter and setter. في بيثون يبدو ظاهريا أن عرض السمات للاستخدام المباشر او للتعديل يكسر مفهوم التغليف. لكننا سنرى أن بيثون يوفر آليات تحمي السمات وفي نفس الوقت تمنح الكود مقروئية اكبر.
هذه الآليات يطلق عليها properties وسنرى عبر بعض الامثلة كيف يوفر بيثون بواسطتها حماية للسمات وتغليفا قويا للكلاسات.
## الكود على طريقة الجافا (للاطلاع فقط): getter and setter
سنبدأ بمثال بسيط. سنبني كلاس مستطيل له عرض وارتفاع. بالطبع يجب أن يكون كلاهما عددين ايجابيين بمعنى ليسا اقل من صفر، وهذا منطقي على ما اعتقد. لنر كيف يفعل القادمون من السي++ او الجافا:
كود :
class Rectangle1:
   def __init__(self, height=0, width=0):
       self.setHeight(height)              # لم نعين قيمة الارتفاع والعرض مباشرة
       self.setWidth(width)                # من باب الاحتياط مررناهما الى الوظيفتين
   def getHeight(self):
       return self._height                # جعلنا سمة الارتفاع 'مخفية' نوعا ما
   def setHeight(self, height):
       self._height = max(0, height)      # هنا نضمن ان الارتفاع يفوق او يساوي 0
   def getWidth(self):
       return self._width                 # جعلنا سمة العرض 'مخفية' نوعا ما
   def setWidth(self, width):
       self._width = max(0, width)        # هنا نضمن أن العرض يفوق او يسوي 0
نجرب
كود :
r1 = Rectangle1(30, 20)
vars(r1)
r2 = Rectangle1(-50, -40)
vars(r2)
r2.setHeight(444)
r2.setWidth(100)
r2.getHeight()
r2.getWidth()
r2.height                   # AttributeError: 'Rectangle1' object has no attribute 'height'
r2._height                 # AttributeError: 'Rectangle1' object has no attribute '_height'
r2.height = 500             # ممكن وهي تتسب في تعريف سمة كائن ديناميكيا
vars(r2)
هكذا يحاول مبرمج قادم من لغات برمجة اخرى حماية السمات. هذا يعمل نوعا ما.. لكننا ذكرنا في موضوع سابق أن الكلاسات والامثلة قابلة للتعديل. لا شيء يمنع من انشاء سمة تحمل اسم height او width او حتى _height مثلا، وعندها يتعقد البرنامج ويصعب تصحيحه.. هذا من ناحية، ومن ناحية أخرى انظر كيف يصبح الكود معقدا، فنحن نحتاج الى استدعاء وظيفة setHeight لتعديل الارتفاع ونحتاج الى getHeight لاستخراجه.
## الكود اليثوني
### الطريقة الاولى
بيثون يستخدم آلية مختلفة تماما لحماية السمات عبر property
كود :
class Rectangle2:
   def __init__(self, height=0, width=0):
       self.height = height                    # عينا قيمة الارتفاع والعرض مباشرة
       self.width = width                      # من دون الحاجة الى الوظيفتين
   def _get_height(self):                      # جعلنا الوظيفة مخفية
       return self._height                     # قمنا بحماية سمة الارتفاع
   def _set_height(self, height):
       self._height = max(0, height)           # هنا نضمن ان الارتفاع يفوق او يساوي 0
   height = property(_get_height, _set_height) # propertyهذه هي تعليمة الـ

   def _get_width(self):                       # جعلنا الوظيفة مخفية
       return self._width                      # قمنا بحماية سمة العرض
   def _set_width(self, width):
       self._width = max(0, width)             # هنا نضمن أن العرض يفوق او يسوي 0
   width = property(_get_width, _set_width)   # propertyهذه هي تعليمة الـ
لنر كيف يعمل الكود الآن:
كود :
r1 = Rectangle2(30, 20)
vars(r1)
r1.height
r1.width
r1.height = 100
r1.width = -70
vars(r1)
r2 = Rectangle2(-50, -40)
vars(r2)
r2.height = 50
r2.width = 40
vars(r2)
r2.height
r2.width
طبعا كان يمكننا ان نجعل الكلاس يطلق استثناء عندما يعترضه عدد سلبي على سبيل المثال عبر
كود :
raise ValueError("height must be a positive number")
لكننا اردنا تفادي التعقيد غير الضروري والتركيز على فهم الـproperties
### الطريقة الثانية
توجد طريقة أخرى تؤدي الى نفس النتائج اي حماية ما نرغب في حمايته من السمات:
كود :
class Rectangle3:
   def __init__(self, height=0, width=0):
       self.height = height                    # عينا قيمة الارتفاع والعرض مباشرة
       self.width = width                      # من دون الحاجة الى الوظيفتين
   @property                                   # propertyهذه هي تعليمة الـ
   def height(self):
       return self._height                     # قمنا بحماية سمة الارتفاع
   @height.setter                              # propertyهذه هي تعليمة الـ
   def height(self, height):
       self._height = max(0, height)           # هنا نضمن ان الارتفاع يفوق او يساوي 0
   @property                                   # propertyهذه هي تعليمة الـ
   def width(self):
       return self._width                      # قمنا بحماية سمة العرض
   @width.setter                               # propertyهذه هي تعليمة الـ
   def width(self, width):
       self._width = max(0, width)             # هنا نضمن أن العرض يفوق او يسوي 0
## اذا كنت ترغب في المزيد من الاطلاع:
https://docs.python.org/3.6/library/func...l#property