View Categories

9.3. نظرة أولى على الفئات

6 دقيقة

9.3. نظرة أولى على الفئات #

تُقدّم الفئات بعضًا من قواعد اللغة الجديدة، وثلاثة أنواع جديدة من الكائنات، وبعض الدلالات الجديدة.

9.3.1. قواعد تعريف الفئة #

يبدو أبسط شكل لتعريف الفئة كما يلي:

class ClassName:

    <statement-1>
    .
    .
    .
    <statement-N>

يجب تنفيذ تعريفات التعيين (جمل التعريف) قبل أن يكون لها أي تأثير. (يمكنك وضع تعريف فئة في فرع من جملة الشرط، أو داخل دالة).

عمليًا، عادةً ما تكون الجمل داخل تعريف الفئة تعريفات دوال، ولكن يُسمح بجمل أخرى، وقد تكون مفيدة أحيانًا – سنعود إلى هذا لاحقًا. عادةً ما يكون لتعريفات الدوال داخل الفئة شكل خاص من قائمة الوسائط، تُمليه قواعد استدعاء الدوال – وسنشرح هذا لاحقًا أيضًا.

عند إدخال تعريف فئة، تُنشأ مساحة اسم جديدة، وتُستخدم كنطاق محلي – وبالتالي، تنتقل جميع تعيينات المتغيرات المحلية إلى هذه المساحة الجديدة. على وجه الخصوص، تربط تعريفات الدوال اسم الدالة الجديدة هنا.

عند ترك تعريف الفئة بشكل طبيعي (عبر النهاية)، يُنشأ كائن فئة. هذا في الأساس غلاف حول محتويات مساحة الاسم التي أنشأها تعريف الفئة؛ سنتعلم المزيد عن كائنات الفئة في القسم التالي. يُعاد تفعيل النطاق المحلي الأصلي (الذي كان ساري المفعول قبل إدخال تعريف الفئة مباشرةً)، ويُربط كائن الفئة هنا باسم الفئة المُعطى في رأس تعريف الفئة (ClassName في المثال).

9.3.2. كائنات الفئة #

تدعم كائنات الفئة نوعين من العمليات: مراجع السمات والإنشاء.

تستخدم مراجع السمات الصيغة القياسية المستخدمة لجميع مراجع السمات في بايثون: obj.name. أسماء السمات الصالحة هي جميع الأسماء التي كانت موجودة في مساحة اسم الفئة عند إنشاء كائن الفئة. لذا، إذا كان تعريف الفئة يبدو كما يلي:

class MyClass:

    """A simple example class"""

    i = 12345

     def f(self):

        return 'hello world'

فإن MyClass.i وMyClass.f هما مراجع سمات صالحة، تُرجعان عددًا صحيحًا وكائن دالة، على التوالي. يمكن أيضًا تعيين سمات الفئة، لذا يمكنك تغيير قيمة MyClass.i عن طريق التعيين. __doc__ هي أيضًا سمة صالحة، تُرجع سلسلة التوثيق الخاصة بالفئة: “مثال بسيط للفئة”.

يستخدم إنشاء مثيل للفئة ترميز الدالة. لنفترض أن كائن الفئة دالة بدون معلمات تُرجع مثيلًا جديدًا للفئة. على سبيل المثال (بافتراض الفئة المذكورة أعلاه):

x = MyClass()

يُنشئ مثيلًا جديدًا للفئة ويُعيّن هذا الكائن للمتغير المحلي x.

عملية إنشاء المثيل (استدعاء كائن فئة) تُنشئ كائنًا فارغًا. تُفضّل العديد من الفئات إنشاء كائنات بمثيلات مُخصصة لحالة ابتدائية مُحددة. لذلك، يُمكن للفئة تعريف دالة خاصة تُسمى __init__()، كما يلي:

def __init__(self):

    self.data = []

عندما تُعرّف فئة دالة __init__()، تستدعي دالة إنشاء المثيل للفئة تلقائيًا __init__() لمثيل الفئة المُنشأ حديثًا. في هذا المثال، يمكن الحصول على مثيل جديد مُهيأ باستخدام:

x = MyClass()

بالطبع، قد تحتوي دالة __init__() على وسيطات لمزيد من المرونة. في هذه الحالة، تُمرر الوسيطات المُعطاة لمُعامل إنشاء المثيل للفئة إلى __init__(). على سبيل المثال،

>>> class Complex:
...     def __init__(self, realpart, imagpart):

...         self.r = realpart

...         self.i = imagpart

...

>>> x = Complex(3.0, -4.5)

>>> x.r, x.i

(3.0, -4.5)

9.3.3. كائنات المثيل #

الآن، ماذا يمكننا أن نفعل بكائنات المثيل؟ العمليات الوحيدة التي تفهمها كائنات المثيل هي مراجع السمات. هناك نوعان من أسماء السمات الصحيحة: سمات البيانات والطُرق.

تتوافق سمات البيانات مع “متغيرات المثيل” في Smalltalk، ومع “عناصر البيانات” في C++. لا يلزم التصريح عن سمات البيانات؛ فكما هو الحال مع المتغيرات المحلية، تظهر هذه السمات عند تعيينها لأول مرة. على سبيل المثال، إذا كان x هو مثيل MyClass الذي تم إنشاؤه أعلاه، فسيطبع الكود التالي القيمة 16 دون ترك أي أثر:

x.counter = 1

while x.counter < 10:

    x.counter = x.counter * 2

print(x.counter)

del x.counter

النوع الآخر من مراجع سمات المثيل هو الدالة. الدالة هي دالة “تنتمي” إلى كائن. (في بايثون، مصطلح الدالة ليس خاصًا بمثيلات الفئة: يمكن أن تحتوي أنواع الكائنات الأخرى على دوال أيضًا. على سبيل المثال، تحتوي كائنات القائمة على دوال تُسمى append وinsert وremove وsort وما إلى ذلك. مع ذلك، في المناقشة التالية، سنستخدم مصطلح الدالة حصريًا للإشارة إلى دوال كائنات مثيل الفئة، ما لم يُنص صراحةً على خلاف ذلك).

تعتمد أسماء الدوال الصحيحة لكائن المثيل على فئته. بحكم التعريف، تُعرّف جميع سمات الفئة التي تُمثل كائنات دالة الدوال المقابلة لمثيلاتها. في مثالنا، x.f مرجع دالة صحيح، لأن MyClass.f دالة، لكن x.i ليس كذلك، لأن MyClass.i ليس كذلك. لكن x.f ليس هو نفسه MyClass.f – إنه كائن دالة، وليس كائن دالة.

9.3.4. كائنات الدالة #

عادةً، تُستدعى الدالة مباشرةً بعد ربطها:

x.f()

في مثال MyClass، سيُرجع هذا السلسلة النصية “hello world”. مع ذلك، ليس من الضروري استدعاء دالة مباشرةً: x.f هو كائن دالة، ويمكن تخزينه واستدعاؤه في وقت لاحق. على سبيل المثال:

xf = x.f

while True:

    print(xf())

سيستمر في طباعة “مرحبًا بالعالم” حتى نهاية الوقت.

ماذا يحدث بالضبط عند استدعاء دالة؟ ربما لاحظتَ أن x.f() استُدعيت بدون مُعامل، على الرغم من أن تعريف الدالة f() حدد مُعاملًا. ماذا حدث للمُعامل؟ من المؤكد أن بايثون تُثير استثناءً عند استدعاء دالة تتطلب مُعاملًا بدون أي مُعامل – حتى لو لم تُستخدم المُعاملة فعليًا…

في الواقع، ربما تكون قد خمنت الإجابة: الميزة المميزة للدوافع هي أن كائن المثيل يُمرر كمعامل أول للدالة. في مثالنا، يُعادل استدعاء x.f() تمامًا استدعاء MyClass.f(x). بشكل عام، يُعادل استدعاء دالة بقائمة من n مُعامل استدعاء الدالة المُقابلة بقائمة مُعاملات تُنشأ عن طريق إدراج كائن مثيل الدالة قبل المُعامل الأول.

إذا كنت لا تزال لا تفهم كيفية عمل الدوال، فربما يُمكنك إلقاء نظرة على طريقة التنفيذ لتوضيح الأمور. عند الإشارة إلى سمة غير بيانات لمثيل، يتم البحث عن صنفه. إذا كان الاسم يدل على سمة صنف صالحة، وهي كائن دالة، يُنشأ كائن دالة عن طريق تجميع (مؤشرات) كائن المثيل وكائن الدالة اللذين تم العثور عليهما للتو في كائن تجريدي: هذا هو كائن الدالة. عند استدعاء كائن الدالة مع قائمة وسيطات، تُنشأ قائمة وسيطات جديدة من كائن المثيل وقائمة الوسيطات، ويُستدعى كائن الدالة مع قائمة الوسيطات الجديدة هذه.

9.3.5. متغيرات الصنف والمثيل #

بشكل عام، تكون متغيرات المثيل خاصة ببيانات فريدة لكل مثيل، بينما تكون متغيرات الصنف خاصة بالسمات والطرق المشتركة بين جميع مثيلات الصنف:

class Dog:

    kind = 'canine'         # class variable shared by all instances

    def __init__(self, name):

        self.name = name    # instance variable unique to each instance

 >>> d = Dog('Fido')

>>> e = Dog('Buddy')

>>> d.kind                  # shared by all dogs

'canine'

>>> e.kind                  # shared by all dogs

'canine'

>>> d.name                  # unique to d

'Fido'

>>> e.name                  # unique to e

'Buddy'

كما هو موضح في “كلمة عن الأسماء والكائنات”، قد يكون للبيانات المشتركة تأثيرات مفاجئة عند استخدام كائنات قابلة للتغيير مثل القوائم والقواميس. على سبيل المثال، لا ينبغي استخدام قائمة الحيل في الكود التالي كمتغير فئة، لأن قائمة واحدة فقط ستُشارك بين جميع نسخ الكلب:

class Dog:

 

    tricks = []             # mistaken use of a class variable

     def __init__(self, name):

        self.name = name

    def add_trick(self, trick):

        self.tricks.append(trick)

>>> d = Dog('Fido')

>>> e = Dog('Buddy')

>>> d.add_trick('roll over')

>>> e.add_trick('play dead')

>>> d.tricks                # unexpectedly shared by all dogs

['roll over', 'play dead']

يجب أن يستخدم التصميم الصحيح للفئة متغير حالة بدلاً من ذلك:

class Dog:

     def __init__(self, name):

        self.name = name

        self.tricks = []    # creates a new empty list for each dog 

    def add_trick(self, trick):

        self.tricks.append(trick)

>>> d = Dog('Fido')

>>> e = Dog('Buddy')

>>> d.add_trick('roll over')

>>> e.add_trick('play dead')

>>> d.tricks

['roll over']

>>> e.tricks

['play dead']
error: Content is protected !!
Scroll to Top