11-06-2018, 01:00 AM
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
هذه الآليات يطلق عليها 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)
## الكود اليثوني
### الطريقة الاولى
بيثون يستخدم آلية مختلفة تماما لحماية السمات عبر 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")
### الطريقة الثانية
توجد طريقة أخرى تؤدي الى نفس النتائج اي حماية ما نرغب في حمايته من السمات:
كود :
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