Author Topic: OOP Basics اساسيات البرمجة الكائنية مع بايثون  (Read 1366 times)

Ahmed Youssef

  • Helping Freak
  • Administrator
  • Active Member
  • *****
  • Posts: 242
    • View Profile
    • WWW
    • Email
OOP

الموضوع هيتحول لصورة ممتعة اكتر بمراحل عن الصفحات اللى فاتت
الدنيا حولينا زى ماهى كلها احتمالات ولوبس فهى كلها OOP X OOP

اولا يعنى ايه OOP ؟
هى اختصار ل Object Oriented Programming وهى اسلوب مختلف فى البرمجة عن اسلوب ال Procedural Programming زى اللى فى السى مثلا وهو بكل بساطة = سهولة وكفاءة اكثر بمراحل من ال Procedural Programming وبكل تأكيد اكتر تنظيم!

اذا بصيت حواليك هتلقة الدنيا كلها عبارة عن Objects عربية انسان عصفورة طيارة قطة كل دول Objects
هندرس على فرض انك بتحدد تصميم لإنسان بتيجى بورقة وقلم كدا وتكتب

الfields هى (كل مايستخدم فى وصف الإنسان)
   اسم
   سن
   نوع
   لون
   طول
   وزن
   ---
   ---

ال methods هى (كل مايؤديه الإنسان)
   يتحرك
   ياكل
   يشرب
   ينام
    ---
   ---   
ال properties هى عبارة عن Encapsulation  (تغليف) للfields لمجرد حماية الصفات الخاصة بالإنسان فهتكون كالتالى
   اسم
   سن
   نوع
   لون
   طول
   وزن
   --
   --
هنتعرض ليها بالتفصيل ان شاء الله فى مثالنا


الصورة المبدأية لل class بتاعنا هتكون كالتالى

Code: [Select]
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 كالتالى

Code: [Select]
    def __init__(self, name, color, sex, height, weight):
        self.name=name
        self.color=color
        self.sex=sex
        self.height=height
        self.weight=weight



جميل تعالى نختبر ال class بتاعنا

Code: [Select]
ahmed=Human("Ahmed", "White", "M", 178,70)

1- اننا ننشئ object من ال Human class ونباصى الداتا اللى هنوصف بيها ال object دا
2- جملة print بسيطة لعرض ال fields

Code: [Select]
print ahmed.name
print ahmed.sex
print ahmed.height
print ahmed.weight
print ahmed.color

جميل الكود بتاعنا 10/10 ولكن فيه مشاكل!

Code: [Select]
ahmed.name=17777777
print ahmed.name

تخيل حد يبقة اسمه عبارة عن ارقام !!! كدا الكود بتاعنا فيه مشكلتين
1- انه مكشوف
2- انه غير آمن اى حد يقدر يحط اى قيم على مزاجه ودا اسمه تهريج
فقدامنا حل جميل جدا وهو اننا نستخدم اسلوب ال get/set ودا اسلوب شهير جدا لحماية الكود
ولكن ازاى نحمى الكود وهو مكشوف ؟ جميل جدا كدا انت بقيت ماشى معايا صح لازم نمنع الأكسس للمستخدم على ال fields بتاعتنا بس ازاى؟!
بسيطة اسبق اسم كل field ب 2 underscores كالتالى مثلا

Code: [Select]
    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 كالتالى

Code: [Select]
 #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 كالتالى مثلا

Code: [Select]
print ahmed.__name

هتلقة error كالتالى

Code: [Select]
    print ahmed.__name
AttributeError: 'Human' object has no attribute '__name'


2- تعالى نجرب نتعامل مع ال fields من خلال ال getters/setters كالتالى مثلا

Code: [Select]
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 ياخد قيمة رقم!
طب وإيه المشكلة ؟ عدل الكود كالتالى مثلا..

Code: [Select]
    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 ليكون  كالتالى

Code: [Select]
        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 مباشرة

Code: [Select]
ahmed.name=new_name 

بدلا من

Code: [Select]
ahmed.set_name(new_name)


Code: [Select]
ahmed.name

بدلا من

Code: [Select]
ahmed.get_name()


الله! ايه دا انت رجعت تانى لموال ال fields مش كنا قلنا اننا لازم نخليها private ونبعدها عن المستخدم؟!
تمام انا قلت كدا بس شكلك مش مركز لأننا بنتكلم عن ال properties  :)
هيكون فى property مثلا بإسم name تقوم بشغل ال get_name و ال set_name كالتالى مثلا

Code: [Select]
    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

Code: [Select]
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

Code: [Select]
>>> class Human(object):
def __init__(self, name):
#Initialize the fields.
self.name=name
self.hands=2

يتم استدعائها فى حال الإنشاء ل object

Code: [Select]
 >>> h1=Human("sami")
>>> h1.name
'sami'
>>> h1.hands
2

فى تعليق اضافى لما ندخل فى الInheritance
2- __getitem__(self, key), __setitem__(self, key, val)
على فرض ان عندنا كلاس بإسم MyDict

Code: [Select]
>>> 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


Code: [Select]
>>> md=MyDict({'Name':'Ahmed', 'Sex':'m'})

نستخدم __getitem__

Code: [Select]
>>> md['Name']  #Call __getitem__('Name')
'Ahmed'

نستخدم __setitem__

Code: [Select]
>>> md['Name']='Youssef'
>>> md['Name']
'Youssef'



3- .__len__(self)
بيها بنحدد سلوك الكلاس بتاعنا مع الدالة الشهيرة len

Code: [Select]
>>> 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


Code: [Select]
>>> l=Lener("Ahmed Youssef", ["Tina", "Salma"])
>>> len(l)  #Calls __len__
13
   

اذا اعدنا تعريفها لتكون كالتالى مثلا

Code: [Select]
	def __len__(self):
return len(self._list)


   فعند استدعاء len على اى اوبجكت من النوع Lener هيتم اعادة عدد عناصر ال self._list

Code: [Select]
>>> l=Lener("Ahmed Youssef", ["Tina", "Salma"])
>>> len(l)
2



4- .__iter__(self)

بتحدد سلوك الكلاس من خلال لتعريفك ل generator وال iterations وتحديدا التعامل مع for loop

Code: [Select]
>>> 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

للتوضيح اكثر

Code: [Select]
>>> i=iter(t)
>>> i.next()
'Python'
>>> i.next()
'Ruby'
>>> i.next()
'Rebol'

*ملحوظة: لازم تتعمل على contatiner


« Last Edit: October 04, 2009, 04:46:49 PM by Ahmed Youssef »
Logged

Life is just a chance to grow a soul. - A. Powell
Weblog: http://ahmedyoussef.wordpress.com/