OOP
الموضوع هيتحول لصورة ممتعة اكتر بمراحل عن الصفحات اللى فاتت
الدنيا حولينا زى ماهى كلها احتمالات ولوبس فهى كلها OOP X OOP
اولا يعنى ايه OOP ؟
هى اختصار ل Object Oriented Programming وهى اسلوب مختلف فى البرمجة عن اسلوب ال Procedural Programming زى اللى فى السى مثلا وهو بكل بساطة = سهولة وكفاءة اكثر بمراحل من ال Procedural Programming وبكل تأكيد اكتر تنظيم!
اذا بصيت حواليك هتلقة الدنيا كلها عبارة عن Objects عربية انسان عصفورة طيارة قطة كل دول Objects
هندرس على فرض انك بتحدد تصميم لإنسان بتيجى بورقة وقلم كدا وتكتب
الfields هى (كل مايستخدم فى وصف الإنسان)
اسم
سن
نوع
لون
طول
وزن
---
---
ال methods هى (كل مايؤديه الإنسان)
يتحرك
ياكل
يشرب
ينام
---
---
ال properties هى عبارة عن Encapsulation (تغليف) للfields لمجرد حماية الصفات الخاصة بالإنسان فهتكون كالتالى
اسم
سن
نوع
لون
طول
وزن
--
--
هنتعرض ليها بالتفصيل ان شاء الله فى مثالنا
الصورة المبدأية لل class بتاعنا هتكون كالتالى
class Human(object):
def __init__(self, name, color, sex, height, weight):
self.name=name
self.color=color
self.sex=sex
self.height=height
self.weight=weight
def eat(self):
#code to eat.
pass
def drink(self):
#code to drink.
pass
def sleep(self):
#code to sleep.
pass
def play(self):
#code to play.
pass
*لكتابة اى class بنبدأ التعريف بكلمة class
*اسم ال class يكون بادئ Uppercase
*ال class بتاعك لازم يورث من Object (هنتكلم عن الوراثة) ولكن يكفيك ان اى انسان ماهو إلاobject واى class فى الدنيا ماهو إلا object فيبقة اكيد هيورث صفات ال Object.
بدأنا ب ال Constructor وهنا فى بايثون بيكون عبارة عن function بإسم __init__
*لاحظ اى method تبدأها ب self .. تقدر تستبدل self بأى كلمة تناسبك ولكن مجتمع بايثون مرتبط ب self فإلتزم بالقواعد.
نبدأ نجهز ال fields بتاعت ال class زى name, sex, color,.. etc
بإننا نعمل field بكل إسم
self.name, self.sex, self.color
ونديه القيم اللى اتباصت من ال constructor كالتالى
def __init__(self, name, color, sex, height, weight):
self.name=name
self.color=color
self.sex=sex
self.height=height
self.weight=weight
جميل تعالى نختبر ال class بتاعنا
ahmed=Human("Ahmed", "White", "M", 178,70)
1- اننا ننشئ object من ال Human class ونباصى الداتا اللى هنوصف بيها ال object دا
2- جملة print بسيطة لعرض ال fields
print ahmed.name
print ahmed.sex
print ahmed.height
print ahmed.weight
print ahmed.color
جميل الكود بتاعنا 10/10 ولكن فيه مشاكل!
ahmed.name=17777777
print ahmed.name
تخيل حد يبقة اسمه عبارة عن ارقام !!! كدا الكود بتاعنا فيه مشكلتين
1- انه مكشوف
2- انه غير آمن اى حد يقدر يحط اى قيم على مزاجه ودا اسمه تهريج
فقدامنا حل جميل جدا وهو اننا نستخدم اسلوب ال get/set ودا اسلوب شهير جدا لحماية الكود
ولكن ازاى نحمى الكود وهو مكشوف ؟ جميل جدا كدا انت بقيت ماشى معايا صح لازم نمنع الأكسس للمستخدم على ال fields بتاعتنا بس ازاى؟!
بسيطة اسبق اسم كل field ب 2 underscores كالتالى مثلا
def __init__(self, name, color, sex, height, weight):
self.__name=name
self.__color=color
self.__sex=sex
self.__height=height
self.__weight=weight
كدا ال fields بقت private مش فى حد يقدر يتعامل معاها غير ال class نفسه فى عملياته الداخلية لكن المستخدم الخارجى لأ
ضيف 2 methods لكل field واحدة get والتانية set كالتالى
#Getters
def get_name(self):
return self.__name
def get_color(self):
return self.__color
def get_sex(self):
return self.__sex
def get_height(self):
return self.__height
def get_weight(self):
return self.__weight
#Setters
def set_name(self, new_name):
self.__name=new_name
def set_color(self, new_color):
self.__color=new_color
def set_height(self, new_height):
self.__height=new_height
def set_weight(self, new_weight):
self.__weight=new_weight
def set_sex(self, new_sex):
self.__sex=new_sex
1- تعالى نختبر موضوع حماية ال fields كالتالى مثلا
هتلقة error كالتالى
print ahmed.__name
AttributeError: 'Human' object has no attribute '__name'
2- تعالى نجرب نتعامل مع ال fields من خلال ال getters/setters كالتالى مثلا
ahmed=Human("Ahmed", "White", "M", 178,70)
print ahmed.get_name()
print ahmed.get_color()
ahmed.set_name("Youssef")
print ahmed.get_name()
ahmed.set_name(141241) #Wut?
print ahmed.get_name()
#Output:
Ahmed
White
Youssef
141241
جميل جدا ولكن بردو set_name مش عملت حاجة سمحت ان name ياخد قيمة رقم!
طب وإيه المشكلة ؟ عدل الكود كالتالى مثلا..
def set_name(self, new_name):
if isinstance(new_name, str):
self.__name=new_name
else:
print "new_name ain't a string!" #and do nothin
هنا بنختبر هل ال new_name عبارة عن اوبجكت من str او لأ
بإستخدام isinstance
او تقدر تعدل اسلوب ال icheck ليكون كالتالى
if type(new_name)==str:
self.__name=new_name
“يعنى هل هو string او لأ"
بإستخدام ال type function بدل isinstance
اذا كان string يبقة عادى نعدل الإسم اذا لأ تطلع رسالة new_name ain't a string ومش يتم اى تعديل!
تقدر تضيف ال rules اللى تناسبك مثلا الطول بتاعها وهل يشمل ارقام او لأ وهكذا... مع باقى ال fields
وهى دى عملية ال encapsulation! وهى بإختصار حماية ال data members او fields او ال attributes الخاصة بال class بإستخدام getters/setters
جميل الإسلوب دا صح مش كدا ؟
لكن بايثون بتقدملك اسلوب ابسط لهندسة ال class بتاعك واستخدامه وهو إستخدام ال Properties
فاكر لما قلنا انها عبارة عن encapsulation لل fields ؟ تمام ال properties بتعمل class لل getter/setter مباشرة
بدلا من
بدلا من
الله! ايه دا انت رجعت تانى لموال ال fields مش كنا قلنا اننا لازم نخليها private ونبعدها عن المستخدم؟!
تمام انا قلت كدا بس شكلك مش مركز لأننا بنتكلم عن ال properties

هيكون فى property مثلا بإسم name تقوم بشغل ال get_name و ال set_name كالتالى مثلا
name=property(fget=get_name, fset=set_name, doc="Gets/Sets the name.")
fget بتعبر عن الميثود المسئولة عن ال get وهنا هتكون get_name وهى بتستدعى فى حال
object.name
fset بتعبر عن الميثود المسئولة عن ال set وهنا هتكون set_name وهتستدعى فى حال
object.name=new_name
doc بتعبر عن وصف ال property
ahmed=Human("Ahmed", "White", "M", 178,70)
print ahmed.get_name()
print ahmed.name
ahmed.name="Youssef"
print ahmed.name
ahmed.name=979878 #uses the get_name rules!
طب كدا فى حاجة ايه الهدف من التكرار فى ؟ ليه يكون عندى name, get_name, set_name اسمح للمستخدم انه يستخدمهم ؟
تمام بكل بساطة اعمل ال get_name, set_name ك private method واتعامل بيها داخل الكلاس وخلى ال name ك property ظاهرة للمستخدم ويتعامل معاها بدل مايتعامل مع 2 methods
ازاى اخليهم private ؟ بكل بساطة اسبق اساميهم ب 2 underscores
ندخل فى ال Magic Methods
بداية هى كل method مبدئة ومنتهية ب __ مثلا __init__
الفائدة: هى بتيح ليك تعريف سلوك التعامل مع ال Builtin Functions زى len مثلا!
بتتيح ليك دعم ال Operator Overloading هنشوف كل الكلام دا بالتفصيل
1- __init__
هى ميثود مسؤلة عن تجهيز ال fields فى حال انشاء ال object
--منتشرة بإسم Constructor
>>> class Human(object):
def __init__(self, name):
#Initialize the fields.
self.name=name
self.hands=2
يتم استدعائها فى حال الإنشاء ل object
>>> h1=Human("sami")
>>> h1.name
'sami'
>>> h1.hands
2
فى تعليق اضافى لما ندخل فى الInheritance
2- __getitem__(self, key), __setitem__(self, key, val)
على فرض ان عندنا كلاس بإسم MyDict
>>> class MyDict(object):
def __init__(self, d={}):
self.__d=d
def __getitem__(self, key):
if key in self.__d.keys():
return self.__d[key]
def __setitem__(self, key, val):
self.__d[key]=val
احنا مثلا مش عايزين نتعامل مباشرة مع ال self.__d ولكن عايزين نتعامل معاه من خلال الكلاس او عملية ال indexing
>>> md=MyDict({'Name':'Ahmed', 'Sex':'m'})
نستخدم __getitem__
>>> md['Name'] #Call __getitem__('Name')
'Ahmed'
نستخدم __setitem__
>>> md['Name']='Youssef'
>>> md['Name']
'Youssef'
3- .__len__(self)
بيها بنحدد سلوك الكلاس بتاعنا مع الدالة الشهيرة len
>>> class Lener(object):
def __init__(self, s, alist):
self._s=s
self._list=alist
def __len__(self):
return len(self._s)
هنا بتعريفنا لل __len__ قمنا بتحديد السلوك فى حال استخدام len مع اى انستنس مع الكلاس دا وهنا هنخليها تعيد عدد حروف ال سترينج self._s
>>> l=Lener("Ahmed Youssef", ["Tina", "Salma"])
>>> len(l) #Calls __len__
13
اذا اعدنا تعريفها لتكون كالتالى مثلا
def __len__(self):
return len(self._list)
فعند استدعاء len على اى اوبجكت من النوع Lener هيتم اعادة عدد عناصر ال self._list
>>> l=Lener("Ahmed Youssef", ["Tina", "Salma"])
>>> len(l)
2
4- .__iter__(self)
بتحدد سلوك الكلاس من خلال لتعريفك ل generator وال iterations وتحديدا التعامل مع for loop
>>> class Tech(object):
def __init__(self, langs, nums):
self._langs=langs
self._nums=nums
def __iter__(self):
for lang in self._langs: yield lang
>>> t=Tech(['Python', 'Ruby', 'Rebol'], [1500, 1414, 12515])
>>> for lang in t:
print lang
للتوضيح اكثر
>>> i=iter(t)
>>> i.next()
'Python'
>>> i.next()
'Ruby'
>>> i.next()
'Rebol'
*ملحوظة: لازم تتعمل على contatiner