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__


→ Functions Procedures OOP Basics OOP Inheritance ←
أدوات شخصية