12. الفئات والكائنات
الفئة هي القالب الذي تُصنع منه الكائنات. ويُقال إن الكائن هو مثيل لفئة ما.

ملاحظة: تم وضع المجلد [shared] ضمن مجلد [Sources Root] الخاص بالمشروع.
12.1. النص البرمجي [classes_01]: فئة «Objet»
يُظهر البرنامج النصي [classes_01] استخدامًا قديمًا للفئات:
# فئة
class Objet(object):
"""une classe Objet vide"""
# أي متغير من النوع [Objet] يمكن أن يكون له سمات بحكم بنيته
obj1 = Objet()
obj1.attr1 = "un"
obj1.attr2 = 100
# يعرض الكائن
print(f"objet1=[{obj1}, {type(obj1)},{id(obj1)},{obj1.attr1},{obj1.attr2}]")
# يُعدّل الكائن
obj1.attr2 += 100
# يعرض الكائن
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
# يُعيّن المرجع obj1 إلى obj2
obj2 = obj1
# يُعدّل الكائن الذي يشير إليه obj2
obj2.attr2 = 0
# يعرض الكائنين - يشير obj1 الآن إلى كائن تم تعديله
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2.attr1},{obj2.attr2}]")
# يشير كل من obj1 و obj2 إلى نفس الكائن
print(f"objet1=[{obj1}, {id(obj1)},{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2}, {id(obj2)},{obj2.attr1},{obj2.attr2}]")
print(obj1 == obj2)
# نوع مثيل obj1
print(f"type(obj1)={type(obj1)}")
print(f"isinstance(obj1,Objet)={isinstance(obj1, Objet)}, isinstance(obj1,object)={isinstance(obj1, object)}")
# كل نوع هو كائن في لغة Python
print(f"type(4)={type(4)}")
print(f"isinstance(4, int)={isinstance(4, int)}, isinstance(4, object)={isinstance(4, object)}")
ملاحظات:
- السطران 2 و3: فئة فارغة باسم [Objet]؛
- السطر 2: يمكن إعلان الفئة بثلاثة أشكال:
- class Objet;
- class Objet()؛
- class Objet(object)؛
- السطر 3: شكل آخر من أشكال التعليقات. هذا التعليق، الذي يسبقه ثلاثة "، يمكن أن يمتد على عدة أسطر؛
- السطر 7: إنشاء مثيل للفئة Objet. والنتيجة هي عنوان كما ستوضح الأسطر 24-26؛
- السطران 8-9: التهيئة المباشرة لخاصيتين من خصائص الكائن؛
- السطر 17: نسخ المراجع. المتغيران obj1 و obj2 هما مؤشران (مراجع) إلى نفس الكائن؛
- السطر 19: يتم تعديل الكائن الذي يشير إليه [obj2]. وبما أن [obj1] و [obj2] يشيران إلى نفس الكائن، فإن عرض الكائنات [obj1, obj2] في السطرين 21 و 22 سيُظهر أن الكائن الذي يشير إليه [obj1] قد تغير؛
- السطور 24-26: تهدف هذه السطور إلى إظهار تساوي المتغيرين [obj1] و [obj2]. سيظهر ذلك عند عرض السطر 26. في هذه المقارنة، العناوين [obj1] و [obj2] هي التي تتساوى؛
- يتم تعريف كل كائن في Python برقم فريد يتم الحصول عليه باستخدام التعبير [id(objet)]. ستُظهر السطران 24 و25 أن أرقام الكائنات التي تشير إليها [obj1] و[obj2] متطابقة، مما يدل على أن هاتين المرجعين يشيران إلى نفس الكائن؛
- السطور 27-29: تُرجع الدالة [isinstance(expr,Type)] القيمة المنطقية True إذا كان التعبير [expr] من النوع [Type]. هنا، سنرى أن [obj1] من النوع [Objet]، وهو ما يبدو طبيعيًا، ولكنه أيضًا من النوع [object]. الفئة [object] هي الفئة الأم لجميع فئات Python. وبحكم خاصية وراثة الفئات، فإن الفئة الفرعية F تمتلك جميع خصائص فئتها الأم P، وتُرجع الدالة [isinstance(instance de F, P)] القيمة True؛
- الأسطر 30-32: توضح أن النوع [int] هو أيضًا نوع [object]. جميع أنواع Python مشتقة من الفئة [object]؛
النتائج
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_01.py
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, <class '__main__.Objet'>,2595221838768,un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]
objet1=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
objet2=[<__main__.Objet object at 0x0000025C3F469BB0>, 2595221838768,un,0]
True
type(obj1)=<class '__main__.Objet'>
isinstance(obj1,Objet)=True, isinstance(obj1,object)=True
type(4)=<class 'int'>
isinstance(4, int)=True, isinstance(4, object)=True
Process finished with exit code 0
12.2. نص برمجي [classes_02]: فئة Personne
يوضح البرنامج النصي [classes_02] أن سمات الفئة عامة: يمكن الوصول إليها مباشرة من خارج الفئة. وهذا مثال آخر على استخدام غير موصى به للفئات. ومع ذلك، نذكره هنا لأنه قد يصادفنا أحيانًا هذا النوع من الكود (تقبله لغة Python)، ولذا يجب أن نكون قادرين على فهمه.
# فئة Personne
class Personne:
# سمات الفئة
# غير مُعلنة - يمكن إنشاؤها ديناميكيًا
# طريقة
def identité(self: object) -> str:
# تستخدم، على ما يبدو، سمات غير موجودة
return f"[{self.prénom},{self.nom},{self.âge}]"
# ---------------------------------- main
# السمات عامة ويمكن إنشاؤها ديناميكيًا
# إنشاء مثيل للفئة
p = Personne()
# التهيئة المباشرة لسمات الفئة
p.prénom = "Paul"
p.nom = "de la Hûche"
p.âge = 48
# استدعاء إحدى طرق الفئة
print(f"personne={p.identité()}")
# نوع المثيل p
print(f"type(p)={type(p)}")
print(f"isinstance(Personne)={isinstance(p, Personne)}, isinstance(object)={isinstance(p, object)}")
ملاحظات:
- الأسطر 2-9: فئة تحتوي على دالة؛
- السطر 7: يجب أن يكون المعلمة الأولى لأي دالة في الفئة هي الكائن self الذي يشير إلى الكائن الحالي. تُرجع الدالة [identité] سلسلة أحرف؛
- السطر 15: إنشاء مثيل لكائن [Personne]؛
- الأسطر 16-19: توضح أن سمات الكائن يمكن إنشاؤها ديناميكيًا (فهي غير موجودة في تعريف الفئة)؛
- السطر 9: تُشار إلى سمات الفئة بالترميز [self.attribut]؛
- ستوضح الأسطر 23-24 أن الكائن [p] هو في الوقت نفسه مثيل للفئة [Personne] وللفئة [object]؛
النتائج
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_02.py
personne=[Paul,de la Hûche,48]
type(p)=<class '__main__.Personne'>
isinstance(Personne)=True, isinstance(object)=True
Process finished with exit code 0
12.3. البرنامج النصي [classes_03]: فئة Personne مع منشئ
يوضح البرنامج النصي [classes_03] الاستخدام العادي لفئة ما:
# فئة Personne
class Personne:
# منشئ - يقوم بتهيئة ثلاث سمات
def __init__(self: object, prénom: str, nom: str, âge: int):
# الاسم الأول: الاسم الأول للشخص
# اللقب: لقب الشخص
# العمر: عمر الشخص
self.prénom = prénom
self.nom = nom
self.âge = âge
# يعرض سمات الفئة في شكل سلسلة أحرف
def identité(self: object) -> str:
return f"[{self.prénom},{self.nom},{self.âge}]"
# ---------------------------------- main
# كائن «شخص»
p = Personne("Paul", "de la Hûche", 48)
# استدعاء طريقة
print(f"personne={p.identité()}")
ملاحظات:
- السطر 4: يُسمى منشئ الفئة __init__. وكما هو الحال مع الطرق الأخرى، فإن المعلمة الأولى له هي self؛
- السطر 20: يتم إنشاء كائن Personne باستخدام منشئ الفئة؛
- الأسطر 13-15: تُرجع الطريقة [identité] سلسلة أحرف تمثل محتوى الكائن؛
- السطر 22: عرض هوية الشخص؛
النتائج
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_03.py
personne=[Paul,de la Hûche,48]
Process finished with exit code 0
12.4. نص برمجي [classes_04]: طرق ثابتة
نقوم في المجلد [modules] بتعريف الفئة [Utils] التالية (utils.py):

class Utils:
# طريقة ثابتة
@staticmethod
def is_string_ok(string: str) -> bool:
# هل string عبارة عن سلسلة
erreur = not isinstance(string, str)
if not erreur:
# هل السلسلة فارغة؟
erreur = string.strip() == ''
# النتيجة
return not erreur
ملاحظات
- السطر 3: تشير التعليقة التوضيحية [@staticmethod] إلى أن الطريقة المُعلَّمة بهذه التعليقة هي طريقة فئة وليست طريقة مثيل. ويتضح ذلك من حقيقة أن المعلمة الأولى للطريقة المُعلَّمة بهذه التعليقة ليست الكلمة المفتاحية [self]. وبالتالي، لا يمكن للطريقة الثابتة الوصول إلى سمات الكائن. بدلاً من كتابة:
نكتب
ونظرًا لأننا كتبنا أعلاه [Utils.is_string_ok]، فإن الدالة [is_string_ok] تُسمى دالة فئة (فئة Utils هنا). ولذلك، يجب توضيح الطريقة [Utils.is_string_ok] باستخدام الكلمة المفتاحية [@staticmethod].
تسمح الطريقة الثابتة [Utils.is_string_ok] هنا بالتحقق من أن البيانات عبارة عن سلسلة أحرف غير فارغة.
يستخدم البرنامج النصي [classes_04] الفئة [Utils] بالطريقة التالية:
# يتم تكوين التطبيق
import config
config = config.configure()
# تم تكوين syspath - يمكن إجراء عمليات الاستيراد
from utilitaires import Utils
print(Utils.is_string_ok(" "))
print(Utils.is_string_ok(47))
print(Utils.is_string_ok(" q "))
- الأسطر 1-4: يتم استخدام برنامج نصي للتكوين؛
نص التكوين [config.py] هو كما يلي:
def configure():
import os
# مجلد ملف التكوين
script_dir = os.path.dirname(os.path.abspath(__file__))
# المسارات المطلقة للمجلدات التي يجب وضعها في syspath
absolute_dependencies = [
f"{script_dir}/shared",
"C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages",
"C:/myprograms/Python38/lib",
"C:/myprograms/Python38/DLLs"
]
# تحديث مسار النظام
from myutils import set_syspath
set_syspath(absolute_dependencies)
# إرجاع التكوين
return {}
- السطر 9: سيتم وضع المجلد [shared] في مسار Python؛
ونتيجة التنفيذ هي كما يلي:
12.5. البرنامج النصي [classes_05]: فحوصات صحة السمات
يقدم البرنامج النصي [classes_05] مفاهيم جديدة:
- تعريف نوع استثناء خاص؛
- تعريف الأسلوب [_str_] الذي يمثل أسلوب الهوية الافتراضي للفئات؛
- تعريف الخصائص؛
# يتم تكوين التطبيق
import config
config = config.configure()
# تم تكوين syspath - يمكن الآن إجراء عمليات الاستيراد
from utilitaires import Utils
# فئة استثناء خاصة مشتقة من [BaseException]
class MyException(BaseException):
# لا يتم القيام بأي شيء: فئة فارغة
pass
# فئة Personne
class Personne:
# منشئ
def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
# الاسم الأول: الاسم الأول للشخص
# اللقب: لقب الشخص
# العمر: عمر الشخص
# تخزين المعلمات
# سيتم إجراء عمليات التهيئة عبر أدوات الضبط
self.prénom = prénom
self.nom = nom
self.âge = âge
# طريقة toString الخاصة بالفئة
def __str__(self: object) -> str:
return f"[{self.__prénom},{self.__nom},{self.__âge}]"
# الطرق المخصصة للاسترداد
@property
def prénom(self) -> str:
return self.__prénom
@property
def nom(self) -> str:
return self.__nom
@property
def âge(self) -> int:
return self.__âge
# أدوات التعيين
@prénom.setter
def prénom(self, prénom: str):
# يجب ألا يكون الاسم الأول فارغًا
if Utils.is_string_ok(prénom):
self.__prénom = prénom.strip()
else:
raise MyException("Le prénom doit être une chaîne de caractères non vide")
@nom.setter
def nom(self, nom: str):
# يجب ألا يكون الاسم الأول فارغًا
if Utils.is_string_ok(nom):
self.__nom = nom.strip()
else:
raise MyException("Le nom doit être une chaîne de caractères non vide")
@âge.setter
def âge(self, âge: int):
# يجب أن يكون العمر عددًا صحيحًا >=0
erreur = False
if isinstance(âge, int):
if âge >= 0:
self.__âge = âge
else:
erreur = True
else:
erreur = True
# خطأ؟
if erreur:
raise MyException("L'âge doit être un entier >=0")
# ---------------------------------- main
# كائن «Personne»
try:
# إنشاء مثيل لفئة «Personne»
p = Personne("Paul", "de la Hûche", 48)
# عرض الكائن p
print(f"personne={p}")
except MyException as erreur:
# عرض الخطأ
print(erreur)
# كائن آخر من فئة «Personne»
try:
# إنشاء مثيل لفئة «Personne»
p = Personne("xx", "yy", "zz")
# عرض الكائن p
print(f"personne={p}")
except MyException as erreur:
# عرض خطأ
print(erreur)
# شخص آخر بدون معلمات هذه المرة
try:
# إنشاء مثيل لفئة «Personne»
p = Personne()
# عرض الكائن p
print(f"personne={p}")
except MyException as erreur:
# عرض رسالة الخطأ
print(erreur)
# لا يمكن الوصول إلى السمات الخاصة __attr للفئة
p.__prénom = "Gaëlle"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")
p.prénom = "Sébastien"
print(f"p.prénom={p.prénom}")
print(f"p.__prénom={p.__prénom}")
ملاحظات:
- الأسطر 10-13: فئة MyException مشتقة من الفئة BaseException (سنناقش هذه النقطة لاحقًا). وهي لا تضيف أي وظائف إلى هذه الأخيرة. إن وجودها يقتصر على توفير استثناء خاص؛
- السطر 19: يحتوي المنشئ على قيم افتراضية لمعلماته. وبالتالي، فإن العملية p=Personne() تعادل p=Personne("x","y",0)؛
- الأسطر 34-45: خصائص الفئة. وهي طرق مُعلَّمة بالكلمة الرئيسية [@property]. تُستخدم لإرجاع قيم السمات؛
- الأسطر 47-77: طرق التعيين الخاصة بالفئة. وهي طرق مُعلَّمة بالكلمة الرئيسية [@attributsetter]. تُستخدم لتعيين قيم السمات؛
- الأسطر 48-54: دالة تعيين السمة [prénom]. سيتم استدعاء هذه الدالة في كل مرة يتم فيها تعيين قيمة للسمة [prénom]:
سيؤدي السطر 2 إلى استدعاء [p.prénom(valeur)]. وتكمن فائدة استخدام دالة التعيين لتعيين قيمة لسمة ما في أن دالة التعيين، باعتبارها دالة، تتيح التحقق من صحة القيمة المعينة للسمة؛
- السطر 51: يتم التحقق من أن القيمة المخصصة للسمة [prénom] هي سلسلة أحرف غير فارغة. وللقيام بذلك، يتم استخدام الطريقة الثابتة [Utils.isStringOk] التي سبق ذكرها؛
- السطر 52: يتم إزالة «المسافات» في بداية ونهاية السلسلة من القيمة المعينة للسمة [prénom]، ثم تُعزى هذه القيمة إلى السمة [self.__prénom]. وبالتالي، فإن السمة [prénom] ليست هي المستخدمة هنا. لم يكن بالإمكان استخدامها، وإلا لكنا واجهنا استدعاءً متكررًا لا نهائيًا. كان من الممكن استخدام أي اسم سمة. إن استخدام السمة [__prénom] مع وجود شرطتين سفليتين في بداية المعرف له معنى خاص: فالسمات التي يسبقها شرطان سفليان هي سمات خاصة بالفئة. وهذا يعني أنها غير مرئية من خارج الفئة. ولذلك لا يمكن كتابة:
في الواقع، سنرى قريبًا أنه يمكن كتابتها، لكنها لا تغير الاسم الأول. بل تؤدي وظيفة أخرى؛
- السطور 53-54: إذا كانت القيمة المعينة للاسم غير صحيحة، يتم إلقاء استثناء. وبذلك سيعرف الكود المستدعي أن استدعاءه غير صحيح؛
- السطور 35-37: الخاصية [prénom]. سيتم استدعاؤها في كل مرة نكتب فيها [p.prénom] في تعبير ما. وعندها سيتم استدعاء الطريقة [p.prénom()]. السطر 37، يتم إرجاع قيمة السمة [__prénom]، حيث رأينا أن مُعيّن السمة [prénom] يعين قيمته إلى السمة الخاصة [__prénom]؛
- الأسطر 56-62: تم إنشاء دالة تعيين السمة [nom] بطريقة مماثلة لتلك الخاصة بالسمة [prénom]. وينطبق الأمر نفسه على دالة تعيين السمة [âge] في الأسطر 64-77؛
- على الرغم من أن الخصائص [prénom, nom, valeur] ليست السمات الحقيقية التي هي في الواقع [__prénom, __nom, __âge]، فسوف نستمر في تسميتها بسمات الفئة، لأنها تُستخدم على هذا النحو؛
- الأسطر 19-28: يستخدم منشئ الفئة ضمنيًا مُعيِّنات القيم للسمات [prénom, nom, âge]. ففي الواقع، عند كتابة [self.prénom = prénom] في السطر 26، سيتم استدعاء الطريقة [prénom(self, prénom)] ضمناً. ثم يتم التحقق من صحة المعلمة [prénom]. وينطبق الأمر نفسه على السمتين الأخريين [nom, âge]؛
- مع هذا النموذج، لا يمكن تعيين قيم غير صحيحة للسمات [prénom, nom, âge] الخاصة بالفئة؛
- الأسطر 30-32: تحل الدالة __str__ محل الطريقة التي كانت تُسمى سابقًا identité. اسم [__str__] (مع وجود شرطتين سفليتين قبله وبعده) ليس عشوائيًا. سنرى ذلك لاحقًا؛
- الأسطر 83-86: إنشاء مثيل لشخص ثم عرض هويته؛
- السطر 84: إنشاء مثيل؛
- السطر 86: العرض. تطلب العملية عرض الشخص p في شكل سلسلة أحرف. عندئذٍ يستدعي مترجم Python تلقائيًا الطريقة p.__str__() إن وجدت. تلعب هذه الطريقة نفس دور الطريقة toString() في لغة Java أو في لغات .NET؛
- الأسطر 87-89: معالجة أي استثناء محتمل من النوع MyException. ثم يعرض الخطأ؛
- الأسطر 91-99: الأمر نفسه بالنسبة لشخص ثانٍ تم إنشاء مثيل له بمعلمات خاطئة؛
- الأسطر 102-109: الأمر نفسه بالنسبة لشخص ثالث تم إنشاء مثيل له باستخدام المعلمات الافتراضية: لا يتم تمرير أي معلمة. وبالتالي، يتم هنا استخدام القيم الافتراضية لهذه المعلمات في المنشئ؛
- الأسطر 112-117: سبق أن ذكرنا أن السمة [__prénom] خاصة، وبالتالي لا يمكن الوصول إليها عادةً من خارج الفئة. نريد التحقق من ذلك؛
- الأسطر 112-114: يتم تعيين قيمة للسمة [__prénom]، ثم يتم التحقق من قيمة السمتين [__prénom] و[prénom] اللتين عادةً ما تكونان متطابقتين؛
- الأسطر 115-117: نكرر العملية، مع تهيئة السمة [prénom] هذه المرة؛
النتائج
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_05.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
personne=[x,y,0]
p.prénom=x
p.__prénom=Gaëlle
p.prénom=Sébastien
p.__prénom=Gaëlle
Process finished with exit code 0
ملاحظات
- الأسطر 5-6: نلاحظ أن التعيين [p.__prénom = "Gaëlle"] لم يغير قيمة السمة [prénom]، السطر 5؛
- السطران 7-8: نلاحظ أن عملية التعيين [p.prénom = "Sébastien"] لم تغير قيمة السمة [__prénom]، السطر 8؛
ما الذي يمكن استنتاجه من ذلك؟ أنه من المحتمل أن العملية [p.__prénom = "Gaëlle"] قد أنشأت سمة عامة [__prénom] للفئة، لكن هذه السمة تختلف عن السمة الخاصة [__prénom] التي تمت معالجتها داخلها؛
12.6. البرنامج النصي [classes_06]: إضافة طريقة لتهيئة الكائن
يضيف البرنامج النصي [classes_06] طريقة إلى الفئة [Personne]:
# يتم تكوين التطبيق
import config
config = config.configure()
# تم تكوين syspath - يمكن إجراء عمليات الاستيراد
from utilitaires import Utils
# فئة استثنائية خاصة مشتقة من [BaseException]
class MyException(BaseException):
# لا يتم القيام بأي شيء: فئة فارغة
pass
# فئة Personne
class Personne:
# منشئ
def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
# الاسم الأول: الاسم الأول للشخص
# اللقب: لقب الشخص
# العمر: عمر الشخص
# تخزين المعلمات
# سيتم إجراء عمليات التهيئة عبر متغيرات الضبط
self.prénom = prénom
self.nom = nom
self.âge = âge
# طريقة أخرى للتهيئة
def init_with_personne(self: object, p: object):
# تُهيئ الكائن الحالي بشخص p
self.__init__(p.prénom, p.nom, p.âge)
# طريقة toString الخاصة بالفئة
def __str__(self: object) -> str:
return f"[{self.__prénom},{self.__nom},{self.__âge}]"
# طرق الاسترجاع
…
# دالات التعيين
…
# ---------------------------------- main
# كائن من فئة Personne
try:
# إنشاء مثيل لفئة «Personne»
p = Personne("Paul", "de la Hûche", 48)
# عرض الكائن p
print(f"personne={p}")
except MyException as erreur:
# عرض رسالة الخطأ
print(erreur)
# كائن آخر من فئة «Personne»
try:
# إنشاء مثيل لفئة Personne
p = Personne("xx", "yy", "zz")
# عرض الكائن p
print(f"p={p}")
except MyException as erreur:
# عرض رسالة الخطأ
print(erreur)
# شخص آخر بدون معلمات هذه المرة
try:
# إنشاء مثيل لفئة «Personne»
p = Personne()
# عرض الكائن p
print(f"p={p}")
except MyException as erreur:
# عرض رسالة الخطأ
print(erreur)
# شخص آخر تم الحصول عليه عن طريق النسخ
try:
# إنشاء مثيل لفئة Personne
p2 = Personne()
p2.init_with_personne(p)
# عرض الكائن p2
print(f"p2={p2}")
except MyException as erreur:
# عرض رسالة الخطأ
print(erreur)
ملاحظات:
- يكمن الاختلاف عن البرنامج النصي السابق في الأسطر 30-33. فقد تمت إضافة الطريقة initWithPersonne. وتستدعي هذه الطريقة منشئ __init__. لا توجد إمكانية، كما هو الحال في اللغات المحددة الأنواع، لوجود طرق تحمل الاسم نفسه وتتميز بطبيعة معلماتها أو نتائجها. وبالتالي، لا توجد إمكانية لوجود عدة منشئات تقوم بإنشاء الكائن بناءً على معلمات مختلفة، وهنا كائن من النوع Personne؛
النتائج
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_06.py
personne=[Paul,de la Hûche,48]
L'âge doit être un entier >=0
p=[x,y,0]
p2=[x,y,0]
Process finished with exit code 0
12.7. نص برمجي [classes_07]: قائمة بكائنات Personne
سنقوم الآن بوضع الفئتين [MyException] و [Personne] في وحدة نمطية حتى نتمكن من استخدامهما دون الحاجة إلى نسخ كودهما:

ستكون الفئتان موجودتين في الوحدة النمطية [myclasses.py] المذكورة أعلاه.
يوضح البرنامج النصي [classes_07] أنه يمكن الحصول على قائمة بالكائنات:
# يتم تكوين التطبيق
import config
config = config.configure()
# تم تكوين syspath - يمكن إجراء عمليات الاستيراد
from myclasses import Personne
# ---------------------------------- main
# إنشاء قائمة بكائنات "person"
groupe = [Personne("Paul", "Langevin", 48), Personne("Sylvie", "Lefur", 70)]
# هوية هؤلاء الأشخاص
for i in range(len(groupe)):
print(f"groupe[{i}]={groupe[i]}")
ملاحظات:
- السطر 7: يتم استيراد الفئة [Personne]؛
- السطر 11: قائمة كائنات من النوع [Personne]؛
- السطران 13-14: يتم تصفح هذه القائمة لعرض كل عنصر من عناصرها؛
- السطر 14: ستعرض الدالة [print] السلسلة التي تمثل الكائن [groupe[i]]. بشكل افتراضي، سيتم استدعاء الطريقة [__str__] الخاصة بهذه الكائنات؛
النتائج
C:\Users\serge\.virtualenvs\cours-python-v02\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/v-02/classes/01/classes_07.py
groupe[0]=[Paul,Langevin,48]
groupe[1]=[Sylvie,Lefur,70]
Process finished with exit code 0
12.8. نص برمجي [classes_08]: إنشاء فئة مشتقة من فئة Personne
نقوم في الوحدة النمطية [myclasses] بتعريف الفئة [Enseignant] التالية:
# فئة «المعلم»
class Enseignant(Personne):
# المنشئ
def __init__(self, prénom: str = "x", nom: str = "x", âge: int = 0, discipline: str = "x"):
# الاسم الأول: الاسم الأول للشخص
# اللقب: لقب الشخص
# العمر: عمر الشخص
# المادة: المادة التي يدرسها
# تعيينات الوالد
Personne.__init__(self, prénom, nom, âge)
# أحرف أولية أخرى
self.discipline = discipline
# toString
def __str__(self) -> str:
return f"enseignant[{super().__str__()},{self.discipline}]"
# الخصائص
@property
def discipline(self) -> str:
return self.__discipline
@discipline.setter
def discipline(self, discipline: str):
# يجب أن تكون «discipline» سلسلة غير فارغة
if Utils.is_string_ok(discipline):
self.__discipline = discipline
else:
raise MyException("La discipline doit être une chaîne de caractères non vide")
- السطر 2: يعلن الفئة Enseignant كفئة مشتقة من الفئة Personne. تحتوي الفئة المشتقة على جميع خصائص (السمات والطرق) الفئة الأم بالإضافة إلى الخصائص الخاصة بها؛
- السطر 13: تُعرِّف الفئة [Enseignant] سمة جديدة هي [discipline]؛
- السطر 11: يجب أن يستدعي منشئ الفئة المشتقة Enseignant منشئ الفئة الأم Personne مع تمرير المعلمات المطلوبة إليه؛
- السطر 17: الدالة [super()] تُرجع الفئة الأصلية. هنا يتم استدعاء الدالة [__str__] الخاصة بالفئة الأصلية؛
- الأسطر 19-30: يتم تعريف دالتي getter و setter للسمة الجديدة [discipline]؛
يستخدم البرنامج النصي [classes_08] الفئة [Enseignant] بالطريقة التالية:
# يتم تكوين التطبيق
import config
config = config.configure()
# تم تكوين syspath - يمكن إجراء عمليات الاستيراد
from myclasses import Personne, Enseignant
# ---------------------------------- main
# إنشاء مصفوفة من كائنات Personne والمشتقات منها
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70)]
# هوية هؤلاء الأشخاص
for i in range(len(groupe)):
print(f"groupe[{i}]={groupe[i]}")
ملاحظات:
- السطر 7: يتم استيراد الفئتين [Personne] و [Enseignant] المحددتين في الملف [myclasses.py]؛
- الأسطر 11-14: يتم تعريف مجموعة من الأشخاص ثم عرض هويتهم؛
النتائج
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_08.py
groupe[0]=enseignant[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]
Process finished with exit code 0
12.9. البرنامج النصي [classes_09]: الفئة الثانية المشتقة من فئة «Personne»
يقدم البرنامج النصي [classes_09] الفئة [Etudiant] المشتقة من الفئة [Personne]. وقد تم تعريف هذه الفئة على النحو التالي في الوحدة النمطية [myclasses]:
# فئة «الطالب»
class Etudiant(Personne):
# المنشئ
def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0, formation: str = "x"):
Personne.__init__(self, prénom, nom, âge)
self.formation = formation
# toString
def __str__(self: object) -> str:
return f"étudiant[{super().__str__()},{self.formation}]"
# الخصائص
@property
def formation(self) -> str:
return self.__formation
@formation.setter
def formation(self, formation: str):
# يجب أن تكون «التدريب» سلسلة غير فارغة
if Utils.is_string_ok(formation):
self.__formation = formation
else:
raise MyException("La formation doit être une chaîne de caractères non vide")
يستخدم البرنامج النصي [classes_09] الفئة [Etudiant] على النحو التالي:
# يتم تكوين التطبيق
import config
config = config.configure()
# تم تكوين syspath - يمكن إجراء عمليات الاستيراد
from myclasses import Personne, Enseignant, Etudiant
# ---------------------------------- main
# إنشاء مصفوفة من كائنات Personne والمشتقات منها
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70),
Etudiant("Steve", "Boer", 22, "iup2 qualité")]
# هوية هؤلاء الأشخاص
for personne in groupe:
# عرض الشخص
print(personne)
ملاحظات:
- هذا البرنامج النصي مشابه للبرنامج النصي السابق.
النتائج
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/classes/01/classes_09.py
enseignant[[Paul,Langevin,48],anglais]
[Sylvie,Lefur,70]
étudiant[[Steve,Boer,22],iup2 qualité]
Process finished with exit code 0
12.10. البرنامج النصي [classes_10]: الخاصية [__dict__]
يقدم البرنامج النصي [classes_10] الخاصية [__dict__] التي سنستخدمها كثيرًا لاحقًا:
# يتم تكوين التطبيق
import config
config = config.configure()
# تم تكوين syspath - يمكن إجراء عمليات الاستيراد
from myclasses import Etudiant
# ---------------------------------- main
# إنشاء طالب
étudiant=Etudiant("Steve", "Boer", 22, "iup2 qualité")
# قاموس الخصائص
print(étudiant.__dict__)
تعليقات
- الأسطر 1-4: يتم تكوين التطبيق؛
- السطر 7: يتم استيراد الفئة [Etudiant]؛
- السطر 11: إنشاء مثيل لطالب؛
- السطر 13: استخدام الطريقة المُعرَّفة مسبقًا [__dict__] (حرفان مُخطَّطان قبل المعرَّف وبعده)؛
والنتائج هي كما يلي:
- في السطر 2، نحصل على قاموس تكون مفاتيحه هي خصائص الكائن مسبوقة باسم الفئة التي تنتمي إليها. سنستخدم هذا القاموس لإنشاء جسر بين الكائن والقاموس؛