PythonGuide/OOP Basics
من PFWiki
محتويات |
اساسيات البرمجة الكائنية
مقدمة
الموضوع هيتحول لصورة ممتعة اكتر بمراحل عن الصفحات اللى فاتت الدنيا حولينا زى ماهى كلها احتمالات ولوبس فهى كلها 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
تجربة الصف
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 كالتالى مثلا
print ahmed.__nameهتلقة 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
استخدام الخصائص Properties
جميل الإسلوب دا صح مش كدا ؟ لكن بايثون بتقدملك اسلوب ابسط لهندسة ال class بتاعك واستخدامه وهو إستخدام ال Properties فاكر لما قلنا انها عبارة عن encapsulation لل fields ؟ تمام ال properties بتعمل class لل getter/setter مباشرة
ahmed.name=new_nameبدلا من
ahmed.set_name(new_name)
ahmed.nameبدلا من
ahmed.get_name()
الله! ايه دا انت رجعت تانى لموال ال 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 مبدئة ومنتهية ب2 اندرسكور مثلا __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'
- ملحوظة: لازم تتعمل على حاوية container
التحميل الزائد Operator Overloading
1+4 دى إستخدام ال+Operator وهو إن يجمع عددين 1*2 إستخدام ال Operator * هنا إنه يضرب عددين 1-2 إستخدام ال – Operator هنا إنه يطرح عددين ولكن !
هل ينفع يكون ل Operator اكثر من إستخدام ؟ اها مثلا + Operator بيستخدم فى عمل دمج بين ال Strings
>>> s1='Hello, ' >>> s2='World!' >>> s=s1+s2 >>> s 'Hello, World!'
يعنى إستخدمنا ال + Operator فى وظيفة اخرى غير الجمع وهى الدمج دى بإختصار هى ال Overloading Operators .. يعنى يكون ل Operator اكثر من إستخدام.
فى Special Methods او بتسمى احيانا بال Magical Methods هى اللى بتوفرلنا موضوع ال Operator Overloading دا + بعض الأشياء الأخرى __add__ للجمع __sub__ للطرح __mul__ للضرب وهكذا
فلنفترض إن عندى class وليكن Worker مثلا
class Worker(object): def __init__(self, name, work_hours): self.name=name self.work_hours=work_hours
وانت عايز تعمل زيادة لساعات العمل work_hours او نقصان او مضاعفة ؟! فى عدة حلول زى إنك تعمل 3 Methods كالتالى مثلا
def increment_workinghours(self, hours): self.work_hours += hours return self.work_hours def decrement_workinghours(self, hours): self.work_hours -= hours return self.work_hours def mul_workinghours(self, hours): self.work_hours *= hours return self.work_hours
حل آخر : هو إنك تعمل Overload لل Operators ال + و – و * كالتالى
def __add__(self, hours): self.work_hours += hours return self.work_hours def __sub__(self, hours): self.work_hours -= hours return self.work_hours def __mul__(self, hours): self.work_hours *=hours return self.work_hours
هيكون صورة الكلاس كالتالى
class Worker(object): def __init__(self, name, work_hours): self.name=name self.work_hours=work_hours def increment_workinghours(self, hours): self.work_hours += hours return self.work_hours def decrement_workinghours(self, hours): self.work_hours -= hours return self.work_hours def mul_workinghours(self, hours): self.work_hours *= hours return self.work_hours def __add__(self, hours): self.work_hours += hours return self.work_hours def __sub__(self, hours): self.work_hours -= hours return self.work_hours def __mul__(self, hours): self.work_hours *=hours return self.work_hours
اعمل Object من ال Class وليكن w
>>> w=Worker('EVAN', 4) >>> w.increment_workinghours(3) 7 >>> w.decrement_workinghours(2) 5 >>> w.mul_workinghours(2) 10
انا شايف إن الإسلوب دا ممل جدا مع إنه احيانا بيكون اءمن بعض الشئ ولكنه ممل!
اعمل Object تانى وليكن w1
>>> w1=Worker('ANN', 5) >>> w1+2 7 >>> w1-4 3 >>> w1*5 15
جدول بكل المعاملات + الMagic Methods الخاصة بيهم لتعريفهم
+ __add__, __radd__ - __sub__, __rsub__ * __mul__, __rmul__ / __div__, __rdiv__, __truediv__ (for Python 2.2), __rtruediv__ (for Python 2.2) // __floordiv__, __rfloordiv__ (for Python version 2.2) % __mod__, __rmod__ ** __pow__, __rpow__ << __lshift__, __rlshift__ >> __rshift__, __rrshift__ & __and__, __rand__ ^ __xor__, __rxor__ | __or__, __ror__ += __iadd__ -= __isub__ *= __imul__ /= __idiv__, __itruediv__ (for Python version 2.2) //= __ifloordiv__ (for Python version 2.2) %= __imod__ **= __ipow__ <<= __ilshift__ >>= __irshift__ &= __iand__ ^= __ixor__ |= __ior__ == __eq__ !+, <> __ne__ > __gt__ < __lt__ >= __ge__ <= __le__
