Skip to content

12. الفئات والكائنات

الفئة هي القالب الذي يتم من خلاله إنشاء الكائنات. ويُقال إن الكائن هو مثيل لفئة ما.

Image

ملاحظة: تم وضع المجلد [shared] في [Root Sources] للمشروع.

12.1. النص البرمجي [classes_01]: فئة كائن

يوضح البرنامج النصي [classes_01] استخدامًا قديمًا للفئات:

#  a class
class Objet(object):
    """une classe Objet vide"""


#  any variable of type [Object] can have attributes by construction
obj1 = Objet()
obj1.attr1 = "un"
obj1.attr2 = 100
#  displays the
print(f"objet1=[{obj1}, {type(obj1)},{id(obj1)},{obj1.attr1},{obj1.attr2}]")
#  modify object
obj1.attr2 += 100
#  displays the
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
#  assign reference obj1 to obj2
obj2 = obj1
#  modifies the object pointed to by obj2
obj2.attr2 = 0
#  displays both objects - obj1 now points to a modified object
print(f"objet1=[{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2.attr1},{obj2.attr2}]")
#  obj1 and obj2 point to the same object
print(f"objet1=[{obj1}, {id(obj1)},{obj1.attr1},{obj1.attr2}]")
print(f"objet2=[{obj2}, {id(obj2)},{obj2.attr1},{obj2.attr2}]")
print(obj1 == obj2)
#  instance type obj1
print(f"type(obj1)={type(obj1)}")
print(f"isinstance(obj1,Objet)={isinstance(obj1, Objet)}, isinstance(obj1,object)={isinstance(obj1, object)}")
#  any type is an object in Python
print(f"type(4)={type(4)}")
print(f"isinstance(4, int)={isinstance(4, int)}, isinstance(4, object)={isinstance(4, object)}")

ملاحظات:

  • السطران 2-3: فئة [Object] فارغة؛
  • السطر 2: يمكن إعلان الفئة بثلاثة أشكال:
    • class Object;
    • class Object();
    • class Object(object);
  • السطر 3: شكل آخر من أشكال التعليقات. هذا الشكل، الذي يسبقه ثلاثة ""، يمكن أن يمتد على عدة أسطر؛
  • السطر 7: إنشاء مثيل لفئة Object. والنتيجة هي عنوان، كما هو موضح في الأسطر 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(object)]. ستُظهر السطران 24 و25 أن معرّفي الكائنين اللذين يشير إليهما [obj1] و[obj2] متطابقان، مما يثبت أن هذين المرجعين يشيران إلى الكائن نفسه؛
  • الأسطر 27–29: تُرجع الدالة [isinstance(expr, Type)] القيمة المنطقية True إذا كان التعبير [expr] من النوع [Type]. هنا، سنرى أن [obj1] من النوع [Object]، وهو أمر يبدو طبيعيًا، ولكنه أيضًا من النوع [object]. فئة [object] هي الفئة الأم لجميع فئات Python. وبحسب خاصية وراثة الفئات، فإن الفئة الفرعية F تمتلك جميع خصائص فئتها الأم P، وتُرجع الدالة [isinstance(instance of 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]: فئة Person

يوضح البرنامج النصي [classes_02] أن سمات الفئة عامة: يمكن الوصول إليها مباشرة من خارج الفئة. هذا مثال آخر على استخدام الفئة غير الموصى به. ومع ذلك، فقد قمنا بتضمينه لأنك قد تصادف هذا النوع من الكود أحيانًا (تسمح به لغة Python)، ويجب أن تكون قادرًا على فهمه.

#  person class
class Personne:
    #  class attributes
    #  undeclared - can be created dynamically

    #  method
    def identité(self: object) -> str:
        #  a priori, uses non-existent attributes
        return f"[{self.prénom},{self.nom},{self.âge}]"


#  ---------------------------------- main
#  attributes are public and can be created dynamically
#  class instantiation
p = Personne()
#  direct initialization of class attributes
p.prénom = "Paul"
p.nom = "de la Hûche"
p.âge = 48
#  call a method of the
print(f"personne={p.identité()}")
#  instance type p
print(f"type(p)={type(p)}")
print(f"isinstance(Personne)={isinstance(p, Personne)}, isinstance(object)={isinstance(p, object)}")

ملاحظات:

  • الأسطر 2–9: فئة تحتوي على طريقة؛
  • السطر 7: يجب أن تحتوي كل دالة في الفئة على الكائن self، الذي يشير إلى الكائن الحالي، كمعلمة أولى لها. تُرجع الدالة [identity] سلسلة نصية؛
  • السطر 15: إنشاء مثيل لكائن [Person]؛
  • الأسطر 16–19: توضح أنه يمكن إنشاء سمات الكائن ديناميكيًا (فهي غير موجودة في تعريف الفئة)؛
  • السطر 9: يُشار إلى سمات الفئة بالرمز [self.attribute]؛
  • السطران 23-24 يوضحان أن الكائن [p] هو مثيل لكل من فئة [Person] وفئة [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]: فئة Person مع منشئ

يوضح البرنامج النصي [classes_03] الاستخدام القياسي للفئة:

#  person class
class Personne:
    #  builder - initializes three attributes
    def __init__(self: object, prénom: str, nom: str, âge: int):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  renders class attributes in string form
    def identité(self: object) -> str:
        return f"[{self.prénom},{self.nom},{self.âge}]"


#  ---------------------------------- main
#  a Person object
p = Personne("Paul", "de la Hûche", 48)
#  method call
print(f"personne={p.identité()}")

ملاحظات:

  • السطر 4: يُسمى منشئ الفئة __init__. وكما هو الحال مع الطرق الأخرى، فإن المعلمة الأولى له هي self؛
  • السطر 20: يتم إنشاء كائن Person باستخدام منشئ الفئة؛
  • الأسطر 13–15: تُرجع الطريقة [identity] سلسلة تمثل محتوى الكائن؛
  • السطر 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]: الطرق الثابتة

نقوم بتعريف فئة [Utils] التالية (utils.py) في المجلد [modules]:

Image

class Utils:
    #  static method
    @staticmethod
    def is_string_ok(string: str) -> bool:
        #  string is a string
        erreur = not isinstance(string, str)
        if not erreur:
            #  is the chain empty?
            erreur = string.strip() == ''
        #  result
        return not erreur

ملاحظات

  • السطر 3: تشير العلامة [@staticmethod] إلى أن الطريقة المُعلَّمة بها هي طريقة فئة، وليست طريقة مثيل. ويتضح ذلك من حقيقة أن المعلمة الأولى للطريقة المُعلَّمة ليست الكلمة الرئيسية [self]. وبالتالي، لا يمكن للطريقة الثابتة الوصول إلى سمات الكائن. بدلاً من كتابة:
u=Utils()
print(u.is_string_ok("abcd")

نكتب

print(Utils.is_string_ok("abcd")

نظرًا لأننا كتبنا [Utils.is_string_ok] أعلاه، فإن الطريقة [is_string_ok] تُسمى طريقة فئة (فئة Utils في هذه الحالة). لتحقيق ذلك، يجب توضيح الطريقة [Utils.is_string_ok] باستخدام الكلمة الرئيسية [@staticmethod].

تسمح لنا الطريقة الثابتة [Utils.is_string_ok] بالتحقق هنا من أن جزءًا من البيانات عبارة عن سلسلة غير فارغة.

يستخدم البرنامج النصي [classes_04] فئة [Utils] على النحو التالي:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
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

    #  configuration file folder
    script_dir = os.path.dirname(os.path.abspath(__file__))

    #  absolute paths of folders to put in the 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"
    ]

    #  update syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    #  return the config
    return {}
  • السطر 9: سيتم إضافة المجلد [shared] إلى مسار Python؛

نتيجة التنفيذ هي كما يلي:

1
2
3
4
5
6
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_04.py
False
False
True

Process finished with exit code 0

12.5. البرنامج النصي [classes_05]: فحوصات صحة السمات

يقدم البرنامج النصي [classes_05] مفاهيم جديدة:

  • تعريف نوع استثناء مخصص؛
  • تعريف طريقة [_str_]، وهي طريقة الهوية الافتراضية للفئات؛
  • تعريف الخصائص؛
#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils


#  a proprietary exception class derived from [BaseException]
class MyException(BaseException):
    #  do nothing: empty classroom
    pass


#  person class
class Personne:
    #  manufacturer
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        #  parameter storage
        #  initializations will be made via setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  method toString of class
    def __str__(self: object) -> str:
        return f"[{self.__prénom},{self.__nom},{self.__âge}]"

    #  getters
    @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

    #  setters
    @prénom.setter
    def prénom(self, prénom: str):
        #  first name must be non-empty
        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):
        #  first name must be non-empty
        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):
        #  age must be an integer >=0
        erreur = False
        if isinstance(âge, int):
            if âge >= 0:
                self.__âge = âge
            else:
                erreur = True
        else:
            erreur = True
        #  mistake?
        if erreur:
            raise MyException("L'âge doit être un entier >=0")


#  ---------------------------------- main
#  a Person object
try:
    #  instantiation Person class
    p = Personne("Paul", "de la Hûche", 48)
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error display
    print(erreur)

#  another object Nobody
try:
    #  instantiation Person class
    p = Personne("xx", "yy", "zz")
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error display
    print(erreur)

#  another person without parameters this time
try:
    #  instantiation Person class
    p = Personne()
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  you cannot access the __attr private attributes of the
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 = Person() تعادل p = Person("x", "y", 0)؛
  • الأسطر 34–45: خصائص الفئة. وهي طرق مزودة بعلامة [@property]. تُستخدم لتعيين قيم السمات؛
  • الأسطر 47–77: مُعيّنات الفئة. وهي طرق مُعلّمة بالكلمة الرئيسية [@attributsetter]. تُستخدم لتعيين قيمة السمات؛
  • الأسطر 48–54: أداة تعيين السمة [first_name]. سيتم استدعاء هذه الطريقة في كل مرة يتم فيها تعيين قيمة للسمة [first_name]:
p=Personne(…)
p.prénom=valeur

السطر 2 سيؤدي إلى استدعاء [p.firstName(value)]. ميزة استخدام مُعيّن لتعيين قيمة لسمة هي أنه، بما أن المُعيّن هو دالة، يمكننا التحقق من صحة القيمة المعينة للسمة؛

  • السطر 51: نتحقق من أن القيمة المعينة للسمة [first_name] هي سلسلة غير فارغة. وللقيام بذلك، نستخدم الطريقة الثابتة [Utils.isStringOk] التي رأيناها سابقًا؛
  • السطر 52: يتم إزالة المسافات البيضاء في بداية ونهاية القيمة المعينة للسمة [first_name] وتعيينها إلى السمة [self.__first_name]. لذلك، لا يتم استخدام السمة [first_name] نفسها هنا. لم يكن بإمكاننا فعل غير ذلك، وإلا لكنا حصلنا على استدعاء متكرر لا نهائي. كان بإمكاننا استخدام أي اسم سمة. حقيقة أننا استخدمنا السمة [__prénom] مع شرطتين سفليتين في بداية المعرف لها معنى خاص: السمات التي تسبقها شرطتان سفليتان هي خاصة بالفئة. وهذا يعني أنها غير مرئية من خارج الفئة. لذلك، لا يمكننا كتابة:
p=Personne(…)
p.__prénom=valeur

في الواقع، سنرى قريبًا أنه يمكنك كتابة ذلك، لكنه لا يغير الاسم الأول. إنه يقوم بشيء آخر؛

  • السطور 53–54: إذا كانت القيمة المعينة لـ first_name غير صحيحة، يتم إلقاء استثناء. بهذه الطريقة، سيعرف الكود المستدعي أن استدعاءه غير صحيح؛
  • الأسطر 35-37: الخاصية [first_name]. سيتم استدعاؤها في كل مرة يتم فيها كتابة [p.first_name] في تعبير. سيتم بعد ذلك استدعاء الطريقة [p.first_name()]. السطر 37: نُرجع قيمة السمة [__first_name]، حيث رأينا أن مُعيّن السمة [first_name] يُعيّن قيمتها إلى السمة الخاصة [__first_name]؛
  • الأسطر 56–62: يتم إنشاء مُعيّن السمة [last_name] بشكل مشابه لمُعيّن السمة [first_name]. وينطبق الأمر نفسه على مُعيّن السمة [age] في الأسطر 64–77؛
  • على الرغم من أن الخصائص [first_name, last_name, value] ليست السمات الفعلية — التي هي في الواقع [__first_name, __last_name, __age] — سنستمر في الإشارة إليها على أنها سمات الفئة، نظرًا لاستخدامها على هذا النحو؛
  • الأسطر 19–28: يستخدم منشئ الفئة ضمنيًا مُعيّنات الخصائص [first_name, last_name, age]. في الواقع، بكتابة [self.first_name = first_name] في السطر 26، يتم استدعاء الطريقة [first_name(self, first_name)] ضمنيًا. سيتم بعد ذلك التحقق من صحة المعلمة [first_name]. وينطبق الأمر نفسه على السمتين الأخريين [last_name، age]؛
  • مع هذا النموذج، لا يمكننا تعيين قيم غير صحيحة لسمات الفئة [first_name، last_name، age]؛
  • الأسطر 30-32: تحل الدالة __str__ محل الطريقة التي كانت تسمى سابقًا identity. اسم [__str__] (شرطتان سفليتان قبله وبعده) ليس عارضًا. سنرى ذلك لاحقًا؛
  • الأسطر 83-86: إنشاء مثيل لشخص، يليه عرض هويته؛
  • السطر 84: إنشاء مثيل؛
  • السطر 86: العرض. تتطلب العملية عرض الكائن p كسلسلة. يقوم مترجم Python تلقائيًا باستدعاء الطريقة p.__str__() إذا كانت موجودة. تخدم هذه الطريقة نفس الغرض الذي تخدمه الطريقة toString() في لغات Java أو .NET؛
  • الأسطر 87–89: معالجة استثناء MyException محتمل. ثم عرض الخطأ؛
  • الأسطر 91–99: نفس ما سبق بالنسبة لشخص ثانٍ تم إنشاء مثيل له بمعلمات غير صحيحة؛
  • الأسطر 102–109: نفس ما سبق بالنسبة لشخص ثالث تم إنشاء مثيل له بمعلمات افتراضية: لم يتم تمرير أي معلمات. ثم يتم استخدام القيم الافتراضية لهذه المعلمات في المنشئ هنا؛
  • الأسطر 112–117: ذكرنا أن السمة [__first_name] كانت خاصة وبالتالي لا يمكن الوصول إليها عادةً من خارج الفئة. نريد التحقق من ذلك؛
  • الأسطر 112–114: نعيّن قيمة للسمة [__first_name]، ثم نتحقق من قيم السمتين [__first_name] و[first_name]، اللتين من المفترض أن تكونا متطابقتين؛
  • الأسطر 115–117: نكرر العملية، ونقوم هذه المرة بتهيئة السمة [first_name]؛

النتائج


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.__first_name = "Gaëlle"] لم يغير قيمة السمة [first_name]، السطر 5؛
  • السطران 7-8: نرى أن التعيين [p.first_name = "Sébastien"] لم يغير قيمة السمة [__first_name]، السطر 8؛

ما الذي يمكننا استنتاجه من هذا؟ أنه من المحتمل أن العملية [p.__first_name = "Gaëlle"] أنشأت سمة عامة [__first_name] للفئة، ولكنها تختلف عن السمة الخاصة [__first_name] التي يتم التعامل معها داخلها؛

12.6. النص البرمجي [classes_06]: إضافة طريقة تهيئة كائن

يضيف البرنامج النصي [classes_06] طريقة إلى فئة [Person]:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from utilitaires import Utils


#  a proprietary exception class derived from [BaseException]
class MyException(BaseException):
    #  do nothing: empty classroom
    pass


#  person class
class Personne:
    #  manufacturer
    def __init__(self: object, prénom: str = "x", nom: str = "y", âge: int = 0):
        #  first name: person's first name
        #  name: person's name
        #  age: age of the person

        #  parameter storage
        #  initializations will be made via setters
        self.prénom = prénom
        self.nom = nom
        self.âge = âge

    #  other initialization method
    def init_with_personne(self: object, p: object):
        #  initializes the current object with a person p
        self.__init__(p.prénom, p.nom, p.âge)

    #  method toString of class
    def __str__(self: object) -> str:
        return f"[{self.__prénom},{self.__nom},{self.__âge}]"

    #  getters
    

    #  setters
    


#  ---------------------------------- main
#  a Person object
try:
    #  instantiation Person class
    p = Personne("Paul", "de la Hûche", 48)
    #  object display p
    print(f"personne={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another object Nobody
try:
    #  instantiation Person class
    p = Personne("xx", "yy", "zz")
    #  object display p
    print(f"p={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another person without parameters this time
try:
    #  instantiation Person class
    p = Personne()
    #  object display p
    print(f"p={p}")
except MyException as erreur:
    #  error msg display
    print(erreur)

#  another Person obtained by recopying
try:
    #  instantiation Person class
    p2 = Personne()
    p2.init_with_personne(p)
    #  object display p2
    print(f"p2={p2}")
except MyException as erreur:
    #  error msg display
    print(erreur)

ملاحظات:

  • يكمن الاختلاف عن البرنامج النصي السابق في الأسطر 30–33. لقد أضفنا طريقة initWithPerson. تستدعي هذه الطريقة منشئ __init__. على عكس اللغات المكتوبة، لا يمكن وجود طرق تحمل نفس الاسم ويتم تمييزها حسب طبيعة معلماتها أو قيمها المرجعة. لذلك، لا يمكن وجود منشئات متعددة تنشئ الكائن من معلمات مختلفة، في هذه الحالة كائن من نوع Person؛

النتائج


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]: قائمة بكائنات Person

سنضع الآن فئتي [MyException] و [Person] في وحدة نمطية حتى نتمكن من استخدامهما دون الحاجة إلى نسخ كودهما:

Image

ستكون كلتا الفئتين في الوحدة النمطية [myclasses.py] أعلاه.

يُظهر البرنامج النصي [classes_07] أنه يمكننا الحصول على قائمة من الكائنات:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne

#  ---------------------------------- main
#  create a list of person objects
groupe = [Personne("Paul", "Langevin", 48), Personne("Sylvie", "Lefur", 70)]
#  identity of these persons
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

ملاحظات:

  • السطر 7: نستورد فئة [Person]؛
  • السطر 11: قائمة من الكائنات من النوع [Person]؛
  • السطران 13-14: نكرر هذه القائمة لعرض كل عنصر من عناصرها؛
  • السطر 14: ستعرض الدالة [print] السلسلة التي تمثل الكائن [group[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]: إنشاء فئة مشتقة من فئة Person

نحدد الفئة [Teacher] التالية في الوحدة النمطية [myclasses]:


# classe Enseignant
class Enseignant(Personne):
    # constructeur
    def __init__(self, prénom: str = "x", nom: str = "x", âge: int = 0, discipline: str = "x"):
        # prénom : prénom de la personne
        # nom : nom de la personne
        # âge : âge de la personne
        # discipline : discpline enseignée
 
        # initialisation du parent
        Personne.__init__(self, prénom, nom, âge)
        # autres initialisations
        self.discipline = discipline
 
    # toString
    def __str__(self) -> str:
        return f"enseignant[{super().__str__()},{self.discipline}]"
 
    # propriétés
    @property
    def discipline(self) -> str:
        return self.__discipline
 
    @discipline.setter
    def discipline(self, discipline: str):
        # la discipline doit être une chaîne non vide
        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: يعلن فئة Teacher كفئة فرعية لفئة Person. تحتوي الفئة الفرعية على جميع خصائص (السمات والطرق) فئتها الأم بالإضافة إلى خصائصها الخاصة؛
  • السطر 13: تُعرّف فئة [Teacher] سمة جديدة [subject]؛
  • السطر 11: يجب أن يستدعي منشئ فئة Teacher المشتقة منشئ فئة Person الأصلية، ممرراً له المعلمات التي يتوقعها؛
  • السطر 17: تستدعي الدالة [super()] الفئة الأصلية. هنا، نستدعي الدالة [__str__] للفئة الأصلية؛
  • الأسطر 19–30: يتم تعريف دالة الحصول (getter) ودالة التعيين (setter) للسمة الجديدة [subject]؛

يستخدم البرنامج النصي [classes_08] فئة [Teacher] على النحو التالي:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne, Enseignant

#  ---------------------------------- main
#  creation of an array of Personne and derived objects
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70)]
#  identity of these persons
for i in range(len(groupe)):
    print(f"groupe[{i}]={groupe[i]}")

ملاحظات:

  • السطر 7: نستورد فئتي [Person] و [Teacher] المحددتين في ملف [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]: الفئة الثانية المشتقة من فئة Person

يقدم البرنامج النصي [classes_09] الفئة [Student] المشتقة من الفئة [Person]. يتم تعريفها على النحو التالي في الوحدة النمطية [myclasses]:


# classe Etudiant
class Etudiant(Personne):
    # constructeur
    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}]"
 
    # propriétés
    @property
    def formation(self) -> str:
        return self.__formation
 
    @formation.setter
    def formation(self, formation: str):
        # la formation doit être une chaîne non vide
        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] فئة [Student] على النحو التالي:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Personne, Enseignant, Etudiant

#  ---------------------------------- main
#  creation of an array of Personne and derived objects
groupe = [Enseignant("Paul", "Langevin", 48, "anglais"), Personne("Sylvie", "Lefur", 70),
          Etudiant("Steve", "Boer", 22, "iup2 qualité")]
#  identity of these persons
for personne in groupe:
    #  person display
    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__]، والتي سنستخدمها كثيرًا لاحقًا:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from myclasses import Etudiant

#  ---------------------------------- main
#  student creation
étudiant=Etudiant("Steve", "Boer", 22, "iup2 qualité")
#  property dictionary
print(étudiant.__dict__)

تعليقات

  • الأسطر 1-4: يتم إعداد التطبيق؛
  • السطر 7: تم استيراد فئة [Student]؛
  • السطر 11: إنشاء مثيل لطالب؛
  • السطر 13: استخدام الطريقة المحددة مسبقًا [__dict__] (شرطتان سفليتان قبل وبعد المعرف)؛

والنتائج هي كما يلي:

1
2
3
4
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_10.py
{'_Personne__prénom': 'Steve', '_Personne__nom': 'Boer', '_Personne__âge': 22, '_Etudiant__formation': 'iup2 qualité'}

Process finished with exit code 0
  • في السطر 2، نحصل على قاموس تكون مفاتيحه هي خصائص الكائن مسبوقة باسم الفئة التي تنتمي إليها. سنستخدم هذا القاموس لإنشاء جسر بين الكائن والقاموس؛