البرمجة كائنية التوجه - أو اختصارًا OOP- هي ميزة للغة البرمجة تسمح لك بجمع الدوال functions والمتغيرات variables معًا في أنواع بيانات data type جديدة، تسمى الأصناف classes، والتي يمكنك من خلالها إنشاء كائنات objects. يمكنك تقسيم البرنامج المترابط إلى أجزاء أصغر يسهل فهمها وتنقيح أخطائها عن طريق تنظيم الشيفرة البرمجية الخاصة بك إلى أصناف.
لا تضيف البرمجة كائنية التوجه تنظيمًا بالنسبة للبرامج الصغيرة، بل تقّدم تعقيدًا لا داعٍ له في بعض الحالات، وعلى الرغم من أن بعض اللغات، مثل جافا، تتطلب منك تنظيم كل الشيفرات البرمجية في أصناف، إلا أن ميزات البرمجة كائنية التوجه في بايثون اختيارية، إذ يمكن للمبرمجين الاستفادة من الأصناف إذا كانوا بحاجة إليها أو تجاهلها. يشير حديث مُبرمج بايثون Jack Diederich في PyCon 2012، "توقف عن كتابة الأصناف"، إلى العديد من الحالات التي يستخدم فيها المبرمجون الأصناف عندما تفي دالة بسيطة بالغرض، ولكن بصفتك مبرمجًا، يجب أن تكون على دراية بأساسيات الأصناف وكيف تعمل، لذلك سنتعرف في هذا المقال على ماهية الأصناف، ولماذا تُستخدَم في البرامج، ومفاهيم البرمجة القائمة عليها. البرمجة كائنية التوجه موضوع واسع، وهذا المقال مجرّد مقدمة.
تشبيه من العالم الحقيقي: تعبئة استمارة
لعلّك ملأت الاستمارات الورقية والإلكترونية عدة مرات في حياتك؛ منها لزيارات الطبيب أو لعمليات الشراء عبر الإنترنت أو للرد على دعوة لحضور حفل زفاف. تُستخدم الاستمارة بمثابة طريقة رسمية من قبل شخص آخر أو منظمة أخرى لجمع المعلومات التي يحتاجونها عنك. تسأل الاستمارات المختلفة أنواعًا متنوعة من الأسئلة، فمثلًا يجب عليك شرح حالتك الطبية الحساسة في استمارة الطبيب، بينما عليك الإبلاغ عن أي ضيوف تحضرهم معك إلى حفل الزفاف في استمارة الحفل.
في بايثون، يكون للصنف والنوع ونوع البيانات المعنى ذاته، ومثال عن ذلك النموذج الورقي أو الإلكتروني؛ فالصنف هو مخطط لكائنات بايثون (وتسمى أيضًا النُسَخ instances)، والتي تحتوي على البيانات الممثّلة لاسم، ويمكن أن يكون هذا الاسم هو مريض الطبيب، أو عملية شراء إلكترونية، أو ضيف حفل زفاف. تشبه الأصناف قالب استمارة فارغة، وتشبه الكائنات التي تُنشأ من ذلك الصنف الاستمارة المملوءة التي تحتوي على بيانات فعلية حول نوع الشيء الذي يمثله النموذج. على سبيل المثال، تُشبه استمارة استجابة دعوة حفل الزفاف RSVP الصنف، في حين تشبه دعوة حفل الزفاف RSVP المملوء الكائن في الشكل 1.
[الشكل 1: تُشبه قوالب استمارة دعوة الزفاف الأصناف، في حين تُشبه الاستمارات المعبأة الكائنات]
يمكنك أيضًا النظر إلى الأصناف والكائنات على أنها جداول بيانات، كما في الشكل 2.
[الشكل 2: جدول بيانات لجميع بيانات دعوات حفل الزفاف]
ستشكل ترويسات الأعمدة الأصناف، وتشكل الصفوف rows الفردية كائنًا.
يأتي غالبًا ذكر الأصناف والكائنات على أنها نماذج بيانات للعناصر في العالم الحقيقي، ولكن لا تخلط بين الخريطة map والمنطقة؛ فما تحتويه الأصناف يعتمد على ما يحتاج البرنامج لفعله. يوضح الشكل 3 بعض الكائنات من أصناف مختلفة تمثل جميعها شخصًا، وتخزن معلومات مختلفة تمامًا باختلاف اسم الشخص.
[الشكل 3: أربعة كائنات مصنوعة من أصناف مختلفة تمثل شخصًا، اعتمادًا على ما يحتاج التطبيق إلى معرفته عن الشخص]
يجب أيضًا أن تعتمد المعلومات الموجودة في أصنافك على احتياجات برنامجك، إذ تستخدم العديد من برامج البرمجة كائنية التوجه التعليمية صنف Car
مثالًا أساسيًا دون الإشارة إلى أن ما يوجد في الصنف يعتمد كليًا على نوع البرنامج الذي تكتبه. لا يوجد هناك ما يدعى صنف Car
العام الذي من الواضح أنه يحتوي على تابع honkHorn()
أو سمة numberOfCupholders
لمجرد أنها خصائص تمتلكها سيارات العالم الحقيقي؛ فقد يكون برنامجك لتطبيق ويب لبيع السيارات أو للعبة فيديو لسباق السيارات أو لمحاكاة حركة المرور على الطرق؛ وقد يكون لصنف السيارات الخاصة بتطبيق الويب لبيع السيارات على الويب سمات milesPerGallon
أو manufacturersSuggestedRetailPrice
(تمامًا كما قد تستخدم جداول بيانات وكالة السيارات هذه كعمود)، لكن لن تحتوي لعبة الفيديو ومحاكاة حركة المرور على الطرق على هذه الأصناف، لأن هذه المعلومات ليست ذات صلة بهما. قد يحتوي صنف السيارات الخاصة بلعبة الفيديو على explodeWithLargeFireball()
، ولكن لن يحتوي تطبيق المحاكاة أو بيع السيارات على الصنف ذاته.
إنشاء كائنات من الأصناف
سبق لك استخدام الأصناف والكائنات في بايثون، حتى لو لم تُنشئ الأصناف بنفسك. تذكّر وحدة datetime
، التي تحتوي على صنف باسم date
، إذ تُمثل كائنات صنف datetime.date
(تسمى أيضًا ببساطة كائنات اdatetime.date
أو كائنات date
) تاريخًا محددًا. أدخل ما يلي في الصدفة التفاعلية Interactive Shell لإنشاء كائن من صنف datetime.date
:
>>> import datetime >>> birthday = datetime.date(1999, 10, 31) # مرّر قيمة السنة والشهر واليوم >>> birthday.year 1999 >>> birthday.month 10 >>> birthday.day 31 >>> birthday.weekday() # يمثّل weekday() تابعًا؛ لاحظ القوسين 6
السمات Attributes -أو يطلق عليها أحيانًا الخاصيات- هي متغيرات مرتبطة بالكائنات. يؤدي استدعاء datetime.date()
إلى إنشاء كائن date
جديد، جرت تهيئته باستخدام الوسطاء 1999, 10, 31
، بحيث يمثل الكائن تاريخ 31 أكتوبر 1999. نعيّن هذه الوسطاء على أنها سمات للصنف date
، وهي year
و month
و day
، التي تحتوي على جميع كائنات date
.
يمكن -باستخدام هذه المعلومات- لتابع الصنف weekday()
حساب يوم الأسبوع. في هذا المثال، تُعاد القيمة 6 ليوم الأحد، لأنه وفقًا لتوثيق بايثون عبر الإنترنت، القيمة المُعادة من weekday()
هي عدد صحيح يبدأ من 0 ليوم الاثنين وينتهي بالعدد 6 ليوم الأحد.
يسرد توثيق بايثون العديد من التوابع الأخرى التي تمتلكها كائنات صنف date
. على الرغم من أن كائن date
يحتوي على سمات وتوابع متعددة، لكنه لا يزال كائنًا واحدًا يمكنك تخزينه في متغير، مثل birthday
في هذا المثال.
إنشاء صنف بسيط- WizCion
دعنا ننشئ صنف WizCoin
الذي يمثل عددًا من العملات في عالم سحري خيالي. فئات هذه العملة هي: knuts، و sickles (بقيمة 29 knuts)، و galleons (بقيمة 17 sickles أو 493 knuts). ضع في حساباتك أن العناصر الموجودة في صنف WizCoin
تمثل كميةً من العملات، وليس مبلغًا من المال. على سبيل المثال، ستُخبرك أنك تمتلك خمسة أرباع سنت وعشرة سنتات بدلًا من 1.35 دولار.
في ملف جديد باسم wizcoin.py، ضِف الشيفرة التالي لإنشاء صنف WizCoin
. لاحظ أن اسم دالة __init__
له شرطتان سفليتان قبل وبعد init
(سنناقش __init__
في " التابعين __init__()
والمعامل self
" لاحقًا):
1 class WizCoin: 2 def __init__(self, galleons, sickles, knuts): """إنشاء كائن WizCoin جديد باستخدام galleons و sickles و knuts""" self.galleons = galleons self.sickles = sickles self.knuts = knuts # ملاحظة: لا يوجد لتوابع ()__init__ قيمة مُعادة إطلاقًا 3 def value(self): """حساب القيمة بفئة knuts في غرض WizCoin لكل العملات""" return (self.galleons * 17 * 29) + (self.sickles * 29) + (self.knuts) 4 def weightInGrams(self): """حساب وزن العملات بالجرام""" return (self.galleons * 31.103) + (self.sickles * 11.34) + (self.knuts * 5.0)
يعرّف هذا البرنامج صنفًا جديدًا يدعى WizCoin
باستخدام التعليمة الأولى class
، ويؤدي إنشاء صنف إلى إنشاء نوع جديد من الكائنات، إذ أن استخدام عبارة class
لتعريف صنف يشبه عبارات def
التي تعرّف دوالًا جديدة. توجد تعريفات لثلاثة توابع داخل كتلة التعليمات البرمجية التي تلي تعليمة class
، هي: __init __()
(اختصارًا للتهيئة)، و value()
، و weightInGrams()
. لاحظ أن جميع التوابع لها معامل أوّل يدعى self
الذي سنكتشفه في القسم التالي.
تكون أسماء الوحدات، مثل wizcoin
في ملف wizcoin.py بأحرف صغيرة عادةً، بينما تبدأ أسماء الأصناف، مثل WizCoin
بحرف كبير، ولكن للأسف، لا تتبع بعض الأصناف في مكتبة بايثون هذا الاصطلاح مثل صنف date
.
للتدرب على إنشاء كائنات جديدة لصنف WizCoin
، ضِف الشيفرة المصدرية التالية في نافذة محرر ملفات منفصلة واحفظ الملف باسم wcexample1.py في المجلد wizcoin.py:
import wizcoin 1 purse = wizcoin.WizCoin(2, 5, 99) # تُمرّر الأعداد الصحيحة إلى التابع ()__init__ print(purse) print('G:', purse.galleons, 'S:', purse.sickles, 'K:', purse.knuts) print('Total value:', purse.value()) print('Weight:', purse.weightInGrams(), 'grams') print() 2 coinJar = wizcoin.WizCoin(13, 0, 0) # تُمرّر الأعداد الصحيحة إلى التابع ()__init__ print(coinJar) print('G:', coinJar.galleons, 'S:', coinJar.sickles, 'K:', coinJar.knuts) print('Total value:', coinJar.value()) print('Weight:', coinJar.weightInGrams(), 'grams')
تُنشئ استدعاءات WizCoin()
كائن WizCoin
وتُنفّذ الشيفرة في دالة__init __()
. نمرّر هنا ثلاثة أعداد صحيحة مثل وسطاء إلى WizCoin()
ثم يُعاد توجيه تلك الوسطاء إلى معاملات __init __()
، تُعيَّن الوسطاء إلى سمات كائنات self.galleons
و self.sickles
و self.knuts
. يجب علينا استيراد wizcoin
ووضع .wizcoin
قبل اسم دالة WizCoin()
تمامًا كما تتطلب دالة time.sleep()
أن تستورد وحدة time
ووضع .time
قبل اسم الدالة أولًا.
عند تنفيذ البرنامج، سيبدو الخرج كما يلي:
<wizcoin.WizCoin object at 0x000002136F138080> G: 2 S: 5 K: 99 Total value: 1230 Weight: 613.906 grams <wizcoin.WizCoin object at 0x000002136F138128> G: 13 S: 0 K: 0 Total value: 6409 Weight: 404.339 grams
إذا تلقيت رسالة خطأ، مثل:
ModuleNotFoundError: No module named 'wizcoin'
تحقق أن الملف يحمل اسم wizcoin.py وأنه موجود في المجلد wcexample1.py ذاته.
لا تمتلك كائنات WizCoin
توضيحات نصية مفيدة، لذلك تعرض طباعة purse
و coinJar
عنوان تخزين بين قوسين (ستتعلم كيفية تغيير لاحقًا).
يمكننا استدعاء التابعين value()
و weightInGrams()
على كائنات WizCoin
التي خصصناها لمتغيري purse
و coinJar
، كما يمكننا استدعاء تابع السلسلة lower()
على كائن سلسلة نصية. تحسب هذه التوابع القيم بناءً على سمات كائنات galleons
و sickles
و knuts
.
يُفيد استخدام الأصناف classes والبرمجة كائنية التوجه في إنتاج شيفرات برمجية أكثر قابلية للصيانة؛ أي شيفرة يسهل قراءتها وتعديلها وتوسيعها مستقبلًا.
التابع __init __()والمعامل self
التوابع هي دوال مرتبطة بكائنات من صنف معين. تذكر أن low()
هو تابع لسلسلة، ما يعني أن استدعائه يكون على كائنات سلسلة نصية string. يمكنك استدعاء lower()
من سلسلة، مثل 'Hello'.lower()
ولكن لا يمكنك استدعائها على قائمة مثل: ['dog', 'cat'].lower()
. لاحظ أيضًا أن التوابع تأتي بعد الكائن، والشيفرة الصحيحة هي 'Hello'.lower()
، وليست lower('Hello')
. على عكس تابع مثل lower()
، لا ترتبط دالة مثل len()
بنوع بيانات واحد؛ إذ يمكنك تمرير سلاسل وقوائم وقواميس وأنواع أخرى كثيرة من الكائنات إلى الدالة len()
.
نُنشئ كائنات عن طريق استدعاء اسم الصنف مثل دالة كما رأيت سابقًا، ويُشار إلى هذه الدالة على أنها دالة بانية constructor (أو باني، أو تُختصر باسم ctor، وتُنطق "see-tore") لأنها تُنشئ كائنًا جديدًا. نقول أيضًا أن الباني يبني نسخةً جديدةً للصنف.
يؤدي استدعاء الباني إلى إنشاء كائن جديد ثم تنفيذ تابع __init __()
، ولا يُطلب من الأصناف أن يكون لديها تابع __init __()
، لكنها تملك هذا دائمًا تقريبًا. تابع __init __()
هو المكان الذي تُعيّن فيه القيم الأولية للسمات عادةً. على سبيل المثال، تذكر أن تابع __init __()
الخاص بالصنف WizCoin
يبدو كما يلي:
def __init__(self, galleons, sickles, knuts): """إنشاء كائن WizCoin جديد باستخدام galleons و sickles و knuts""" self.galleons = galleons self.sickles = sickles self.knuts = knuts # ملاحظة: لا يوجد لتوابع ()__init__ قيمة مُعادة إطلاقًا
عندما يستدعي برنامج wcexample1.py ما يلي: WizCoin (2, 5, 99)
، يبني بايثون كائن WizCoin
جديد، ثم يمرر ثلاثة وسطاء (2
و 5
و 99
) إلى استدعاء __init __()
، لكن للتابع __init __()
أربعة معاملات، هي: self
و galleons
و sickles
و knuts
، والسبب هو أن جميع التوابع لها معامل أول يدعى self
. عندما يُستدعى تابع ما على كائن، يُمرّر الكائن تلقائيًا لمعامل self
، وتُعيَّن بقية الوسطاء للمعاملات بصورة طبيعية.
إذا رأيت رسالة خطأ، مثل:
TypeError: __init__() takes 3 positional arguments but 4 were given
ربما تكون قد نسيت إضافة معامل self
إلى تعليمة def
الخاصة بالتابع.
لا يتعين عليك تسمية المعامل الأول للتابع بالاسم self
، إذ يمكنك تسميته بأي شيء آخر، لكن استخدام self
أمر تقليدي، واختيار اسم مختلف سيجعل الشيفرة الخاصة بك أقل قابلية للقراءة لمبرمجي بايثون الآخرين. عندما تقرأ الشيفرة، فإن وجود self
مثل معامل أول هو أسرع طريقة يمكنك من خلالها تمييز التوابع عن الدوال، وبالمثل، إذا كانت شفرة تابعك لا تحتاج أبدًا إلى استخدام معامل self
، فهذه علامة على أن تابعك يجب أن يكون مجرد دالة.
لا تُعيَّن الوسطاء 2
و 5
و 99
في WizCoin (2, 5, 99)
تلقائيًا إلى سمات الكائن الجديد؛ إذ نحتاج إلى عبارات الإسناد الثلاث في __init __()
لإجراء ذلك. تُسمّى معاملات __init __()
غالبًا باسم السمات ذاته، لكن يشير وجود self
في self.galleons
إلى أنها سمة من سمات الكائن، بينما يُعد galleons
معاملًا.
يعد تخزين وسطاء الباني في سمات الكائن مهمةً شائعةً لتابع __init __()
للأصناف. نفّذ استدعاء datetime.date()
في القسم السابق مهمةً مماثلةً باستثناء أن الوسطاء الثلاثة التي مررناها كانت لسمات year
و month
و day
لكائن date
الذي أُنشئ حديثًا.
لقد سبق لك أن استدعيت الدوال int()
و str()
و float()
و bool()
للتحويل بين أنواع البيانات، مثل str (3.1415)
للحصول على قيمة السلسلة '3.1415'
بناءً على القيمة العشرية 3.1415. وصفنا ما سبق عندها على أنها دوال، لكن int
و str
و float
و bool
في الواقع أصناف، والدوال int()
و str()
و float()
و bool()
هي دوال بانية تعيد عددًا صحيحًا جديدًا أو سلسلة أو عدد عشري أو كائنات منطقية. يوصي دليل أسلوب بايثون باستخدام أحرف كبيرة لأسماء أصنافك، مثل WizCoin
، على الرغم من أن العديد من أصناف بايثون المضمنة لا تتبع هذا الاصطلاح.
يعيد استدعاء دالة الإنشاء WizCoin()
الكائن WizCoin
الجديد، لكن التابع __init __()
لا يحتوي أبدًا على عبارة return
بقيمة مُعادة. تؤدي إضافة قيمة إعادة إلى حدوث هذا الخطأ:
TypeError: __init__() should return None.
السمات
السمات attributes -أو الخاصيات- هي متغيرات مرتبطة بكائن، ويصف توثيق بايثون السمات بأنها "أي اسم يتبع النقطة" على سبيل المثال، لاحظ تعبير birthday.year
في القسم السابق، السمة year
هي اسم يتبع النقطة.
يمتلك كل كائن مجموعة السمات الخاصة به، فعندما أنشأ برنامج wcexample1.py كائنين WizCoin
وخزّنهما في متغيرات purse
و coinJar
كان لسماتهما قيم مختلفة. يمكنك الوصول إلى هذه السمات وتعيينها تمامًا مثل أي متغير. للتدرب على إعداد السمات: افتح نافذة محرر ملفات جديدة وأدخل الشيفرة التالية، واحفظها بالاسم wcexample2.py في مجلد الملف wizcoin.py ذاته:
import wizcoin change = wizcoin.WizCoin(9, 7, 20) print(change.sickles) # تطبع 7 change.sickles += 10 print(change.sickles) # تطبع 17 pile = wizcoin.WizCoin(2, 3, 31) print(pile.sickles) # تطبع 3 pile.someNewAttribute = 'a new attr' # إنشاء سمة جديدة print(pile.someNewAttribute)
عند تنفيذ هذا البرنامج، يبدو الخرج كما يلي:
7 17 3 a new attr
يمكنك التفكير في سمات الكائن بطريقة مشابهة لمفاتيح القاموس، إذ يمكنك قراءة وتعديل القيم المرتبطة بها وتعيين سمات جديدة للكائن، تقنيًا تُعدّ التوابع سمات للأصناف أيضًا.
السمات والتوابع الخاصة
يمكن تمييز السمات على أنها تتمتع بوصول خاص في لغات مثل C++ أو جافا، ما يعني أن المصرِّف compiler أو المُفسر interpreter يسمح فقط للشيفرة الموجودة في توابع الأصناف بالوصول إلى سمات كائنات تلك الصنف فقط أو تعديلها، لكن هذا الأمر غير موجود في بايثون، إذ تمتلك جميع السمات والتوابع وصولًا عامًا public access فعال، ويمكن للشيفرة خارج الصنف الوصول إلى أي سمة وتعديلها في أي كائن من ذلك الصنف.
الوصول الخاص مفيد، إذ يمكن مثلًا أن تحتوي كائنات صنف BankAccount
على سمة balance
التي لا يجب الوصول إليها إلا لتوابع صنف BankAccount
. لهذه الأسباب، ينص اصطلاح بايثون على بدء أسماء السمات أو التوابع الخاصة بشرطة سفلية واحدة. تقنيًا، لا يوجد ما يمنع الشيفرة خارج الصنف من الوصول إلى السمات والتوابع الخاصة، ولكن من الممارسات المُثلى تُملي بالسماح لتوابع الصنف فقط بالوصول إليها.
افتح نافذة محرر ملفات جديدة، وأدخل الشيفرة التالية، واحفظها باسم privateExample.py. تحتوي كائنات صنف BankAccount
في هذه الشيفرة على السمتين _name
و _balance
الخاصتين والتي يمكن فقط لتابعَي deposit()
و withdraw()
الوصول إليهما مباشرةً:
class BankAccount: def __init__(self, accountHolder): # يمكن لتوابع BankAccount الوصول إلى self._balance ولكن الشيفرة خارج هذا الصنف لا يمكنها الوصول 1 self._balance = 0 2 self._name = accountHolder with open(self._name + 'Ledger.txt', 'w') as ledgerFile: ledgerFile.write('Balance is 0\n') def deposit(self, amount): 3 if amount <= 0: return # لا تسمح بقيم سالبة self._balance += amount 4 with open(self._name + 'Ledger.txt', 'a') as ledgerFile: ledgerFile.write('Deposit ' + str(amount) + '\n') ledgerFile.write('Balance is ' + str(self._balance) + '\n') def withdraw(self, amount): 5 if self._balance < amount or amount < 0: return # لا يوجد نقود كافية في الحساب أو أن الرصيد سالب self._balance -= amount 6 with open(self._name + 'Ledger.txt', 'a') as ledgerFile: ledgerFile.write('Withdraw ' + str(amount) + '\n') ledgerFile.write('Balance is ' + str(self._balance) + '\n') acct = BankAccount('Alice') # أنشأنا حساب خاص بأليس acct.deposit(120) # يمكن تعديل السمة _balance باستخدام deposit() acct.withdraw(40) # يمكن تعديل السمة _balance باستخدام withdraw() # التغيير من _name و _balance أمر غير محبّذ ولكنه ممكن 7 acct._balance = 1000000000 acct.withdraw(1000) 8 acct._name = 'Bob' # نستطيع الآن التعديل على سجل Bob! acct.withdraw(1000) # عملية السحب هذه مسجلة في BobLedger.txt!
عند تنفيذ privateExample.py، تكون الملفات التي تُنشأ غير دقيقة لأننا عدّلنا على _balance
و _name
خارج الصنف، مما أدى إلى حالات غير صالحة. يحتوي AliceLedger.txt على الكثير من المال بداخله:
Balance is 0 Deposit 120 Balance is 120 Withdraw 40 Balance is 80 Withdraw 1000 Balance is 999999000
يوجد الآن ملف BobLedger.txt برصيد حساب لا يمكن تفسيره، على الرغم من أننا لم ننشئ كائن BankAccount
لسجل Bob إطلاقًا:
Withdraw 1000 Balance is 999998000
تكون الأصناف المصممة جيدًا في الغالب قائمة بحد ذاتها self-contained، مما يوفر توابع لضبط السمات على القيم الصحيحة. تُميَّز السمتين _balance
و _name
برقمي السطرين 1 و2، والطريقة الصالحة الوحيدة لتعديل قيمة صنف BankAccount
هي من خلال التابعين deposit()
و withdraw()
؛ إذ يحقق هذان التابعان من تعليمة (3) وتعليمة (5) للتأكد من أن _balance
لم توضع في حالة غير صالحة (مثل قيمة عدد صحيح سالب). يسجل هذان التابعان أيضًا كل معاملة لحساب الرصيد الحالي في تعليمة (4) وتعليمة (6).
يمكن أن تضع الشيفرة البرمجية التي تعدل هذه السمات وتقع خارج الصنف، مثل تعليمة acct._balance = 1000000000
(التعليمة 7) أو تعليمة acct._name = 'Bob'
(التعليمة ? ذلك الكائن في حالة غير صالحة ويتسبب بأخطاء وعمليات تدقيق من فاحص البنك. يصبح تصحيح الأخطاء أسهل باتباع اصطلاح بادئة الشرطة السفلية للوصول الخاص، والسبب هو أنك تعرف أن سبب الخطأ سيكون داخل شيفرة الصنف بدلًا من أي مكان في البرنامج بأكمله.
لاحظ أنه على عكس جافا واللغات الأخرى، لا تحتاج بايثون إلى توابع getter
و setter
العامة للسمات الخاصة، وتستخدم بدلًا من ذلك الخاصيات properties، كما هو موضح لاحقًا.
دالة type() وسمة qualname
يخبرنا تمرير كائن إلى دالة type()
المضمنة بنوع بيانات الكائن من خلال قيمته المُعادة، والكائنات التي تُعاد من دالة type()
هي أنواع كائنات، وتسمى أيضًا كائنات الصنف. تذكر أن مصطلح النوع ونوع البيانات والصنف لها المعنى ذاته في بايثون. لمعرفة ما تُعيده دالة type()
للقيم المختلفة، أدخل ما يلي في الصدفة التفاعلية:
>>> type(42) # The object 42 has a type of int. <class 'int'> >>> int # int is a type object for the integer data type. <class 'int'> >>> type(42) == int # Type check 42 to see if it is an integer. True >>> type('Hello') == int # Type check 'Hello' against int. False >>> import wizcoin >>> type(42) == wizcoin.WizCoin # Type check 42 against WizCoin. False >>> purse = wizcoin.WizCoin(2, 5, 10) >>> type(purse) == wizcoin.WizCoin # Type check purse against WizCoin. True
لاحظ أن int
هو نوع كائن وهو نفس نوع الكائن الذي يُعيده type(42)
، ولكن يمكن أيضًا تسميته بدالة بانية int()
؛ إذ لا تحوّل الدالة int ('42')
وسيط السلسلة '42'
، وتُعيد بدلًا من ذلك كائن عدد صحيح بناءً على المعطيات.
لنفترض أنك بحاجة إلى تسجيل بعض المعلومات حول المتغيرات في برنامجك لمساعدتك على تصحيحها لاحقًا. يمكنك فقط كتابة سلاسل إلى ملف السجل، ولكن تمرير كائن النوع إلى str()
سيعيد سلسلة تبدو فوضوية إلى حد ما. بدلًا من ذلك، استخدم السمة __qualname__
، التي تمتلكها جميع أنواع الكائنات، لكتابة سلسلة أبسط يمكن للبشر قراءتها:
>>> str(type(42)) # Passing the type object to str() returns a messy string. "<class 'int'>" >>> type(42).__qualname__ # The __qualname__ attribute is nicer looking. 'int'
تُستخدم سمة __qualname__
غالبًا لتجاوز تابع __repr __()
، والتي سنشرحها بمزيد من التفصيل لاحقًا.
الخلاصة
البرمجة كائنية التوجه هي ميزة مفيدة لتنظيم الشيفرة البرمجية الخاصة بك. تتيح لك الأصناف تجميع البيانات والشيفرات البرمجية معًا في أنواع بيانات جديدة. يمكنك أيضًا إنشاء كائنات من هذه الأصناف عن طريق استدعاء بانيها (اسم الصنف المُستدعى مثل دالة)، والتي بدورها تستدعي تابع __init __()
الخاص بالصنف. التوابع هي دوال مرتبطة بالكائنات، والسمات هي متغيرات مرتبطة بالكائنات. تحتوي جميع التوابع على معامل أول self
، والذي يُعيّن للكائن عند استدعاء التابع. يسمح هذا للتوابع بقراءة سمات الكائن أو تعيينها واستدعاء توابعها.
على الرغم من أن بايثون لا تسمح لك بتحديد الوصول الخاص أو العام للسمات، إلا أنها تمتلك اصطلاحًا باستخدام بادئة شرطة سفلية لأي تابع أو سمات يجب استدعاؤها أو الوصول إليها فقط من توابع الصنف الخاصة. يمكنك -باتباع هذه الاتفاقية- تجنب إساءة استخدام الصنف ووضعها في حالة غير صالحة يمكن أن تسبب أخطاء. سيعيد استدعاء type(obj)
كائن صنف النوع obj
. تحتوي كائنات الصنف على سمة __qualname___
التي تحتوي على سلسلة بشكل يمكن للبشر قراءته من اسم الصنف.
في هذه المرحلة، ربما تفكر، لماذا يجب أن نهتم باستخدام الأصناف والسمات والتوابع بينما يمكننا إنجاز المهمة ذاتها مع الدوال؟ تُعد البرمجة كائنية التوجه طريقةً مفيدةً لتنظيم الشيفرات البرمجية الخاصة بك في أكثر من مجرد ملف ".py" يحتوي على 100 دالة فيه. من خلال تقسيم البرنامج إلى عدة أصناف مصممة جيدًا، يمكنك التركيز على كل صنف على حدة.
البرمجة كائنية التوجه هي نهج يركز على هياكل البيانات وطرق التعامل مع هياكل البيانات تلك. هذا النهج ليس إلزاميًا لكل برنامج، ومن الممكن بالتأكيد الإفراط في استخدام البرمجة كائنية التوجه، لكن البرمجة كائنية التوجه توفر فرصًا لاستخدام العديد من الميزات المتقدمة التي سنستكشفها في الفصلين التاليين. أول هذه الميزات هو الوراثة inheritance التي سنتعمق فيها في الفصل التالي.
ترجمة -وبتصرف- لقسم من الفصل Object-Oriented Programming And Classes من كتاب Beyond the Basic Stuff with Python.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.