التوابع

13.3 التوابع

أهداف التعلم

في نهاية هذا القسم، ينبغي أن تكون قادرًا على:

  • كتابة توابع مُعاد تعريفها لتغيير سلوك التوابع الموروثة.
  • استخدام super() للوصول إلى توابع الصنف الأب.
  • تحديد تطبيقات تعدد الأشكال في استخدام التوابع والدوال.

إعادة تعريف التوابع

أحيانًا يرغب المبرمج في تغيير الدوال التي يرثها الصنف الفرعي. Mint هو صنف فرعي يشبه في وظائفه Plant، باستثناء دالة واحدة. يمكن للصنف الفرعي إعادة التعريف (override) لتابع من الصنف الأب عن طريق تعريف تابع بالاسم نفسه لتابع الصنف الأب.

نقطة تحقق

إعادة تعريف تابع الصنف الأب

مفاهيم تطبيقية

إعادة تعريف التوابع

1.
افترض أن مبرمجًا يرث الصنف ContractTax من الصنف Tax ويريد إعادة تعريف التابع calc_tax() الخاص بـ Tax. ماذا ينبغي للمبرمج فعله؟
  1. تعريف تابع calc_tax() آخر في Tax.
  2. تعريف تابع calc_tax() في ContractTax.
  3. تعريف دالة تأخذ مثيلاً من ContractTax.
2.
ما الخطأ في البرنامج الذي يحاول إعادة تعريف calc_tax() للصنف ContractTaxDE؟
class Tax:
  def calc_tax(self):
    print('Calculating tax')

class ContractTax(Tax):
  def calc_tax(self):
    print('Calculating contract tax')

class ContractTaxDE(ContractTax):
  def calc_tax():
    print('Calculating German contract tax')

my_tax = ContractTaxDE()
my_tax.calc_tax()
  1. يجب أن يرث ContractTaxDE من Tax، وليس من ContractTax.
  2. تعريف ContractTaxDE لـ calc_tax() ينقصه مُعامِل.
  3. لا يمكن لـ ContractTaxDE إعادة تعريف calc_tax() لأن ContractTax قد فعل ذلك بالفعل.
3.
البرنامج التالي لا يعيد تعريف calc_tax(). لماذا؟
class Tax:
  def calc_tax(self):
    print('Calculating tax')
    total = 0 # يُستبدل بالحساب
    return total

class ContractTax:
  def calc_tax(self):
    print('Calculating contract tax')
    total = 0 # يُستبدل بالحساب
    return total

my_tax = ContractTax()
my_tax.calc_tax()
  1. لا يمكن لتابع مُعاد تعريفه أن يُرجع قيمة.
  2. لا يحدد Tax أن calc_tax() يمكن إعادة تعريفه.
  3. ليس ContractTax موروثًا من Tax.

الدالة super()

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

نقطة تحقق

استخدام super() لاستدعاء التابع __init__() الخاص بالصنف الأب

مفاهيم تطبيقية

استخدام super()

تأمل البرنامج التالي.

1
class Polygon:
2
  def __init__(self, num_sides=3):
3
    self.num_sides = num_sides
4
5
class Rectangle(Polygon):
6
  def __init__(self, ln=1, wd=1):
7
    super().__init__(4)
8
    self.length = ln
9
    self.width = wd
10
11
class Square(Rectangle):
12
  def __init__(self, side=1):
13
    super().__init__(side, side)
14
    self.side = side
15
16
sq_1 = Square(5)
17
print(sq_1.num_sides)
4.
يُنفَّذ السطر 16 فيُستدعى التابع __init__() الخاص بـ Square في السطر 12. ثم يُنفَّذ السطر 13 فيُستدعى التابع __init__() الخاص بالصنف الأب. إلى أي سطر ينتقل سير التنفيذ بعد ذلك؟
  1. 2
  2. 6
  3. 14
5.
يتم تنفيذ السطر التالي. إلى أي سطر ينتقل سير التنفيذ بعد ذلك؟
  1. 2
  2. 6
  3. 14
6.
يعود استدعاء التابع. يُنفَّذ السطران 8 و9 لتهيئة الطول والعرض، ويعود التابع __init__() الخاص بـ Rectangle. إلى أي سطر ينتقل سير التنفيذ بعد ذلك؟
  1. 12
  2. 14
  3. 17
7.
يعود التابع __init__() الخاص بـ Square وينتقل سير التنفيذ إلى السطر 17. ما الناتج؟
  1. 3
  2. 4
  3. 5

تعدد الأشكال

تعدد الأشكال (polymorphism) هو مفهوم وجود أشكال متعددة. في البرمجة، يمكن استخدام اسم واحد لوظائف متعددة. ضمن الوراثة، يُعد تعدد الأشكال أساس إعادة تعريف التوابع، حيث توجد توابع متعددة تحمل نفس الاسم.

مثال 13.1

تعدد الأشكال والوراثة

الاسم display() يقابل توابع متعددة. يحدد نوع صنف الكائن المستدعي أيَّ تابع display() سيُنفَّذ.

    class Plant:
      def display(self):
        print("I'm a plant")

    class Mint(Plant):
      def display(self):
        print("I'm a mint")

    class Lavender(Mint):
      def display(self):
        print("I'm a lavender")

    mint_1 = Mint()
    mint_1.display()

    lavender_1 = Lavender()
    lavender_1.display()

إخراج الشِفرة هو:

    I'm a mint
    I'm a lavender

يمكن أيضًا استخدام تعدد الأشكال مع توابع الأصناف غير المرتبطة. يحدد نوع صنف الكائن المستدعي التابعَ الذي سيُنفَّذ.

مثال 13.2

تعدد الأشكال والتوابع

Tax و ContractTax هما صنفان غير مرتبطان، وكل منهما يُعرّف calc_tax(). لا يتم إعادة تعريف calc_tax() لأن ContractTax ليس موروثًا من Tax.

    class Tax:
      def __init__(self, value):
        self.value = value
  
      def calc_tax(self):
        print("Calculating tax")
        total = 0.10 * self.value  # يُستبدل بالحساب
        return total
  
    class ContractTax:
      def __init__(self, value):
        self.value = value
  
      def calc_tax(self):
          print("Calculating contracts tax")
          total = 0.15 * self.value  # يُستبدل بالحساب
          return total
  
    my_tax = ContractTax(value=1000)  # استبدل 1000 بأي قيمة
    result = my_tax.calc_tax()
    print(f"Total tax: ${result}")

إخراج الشِفرة هو:

    Calculating contracts tax
    Total tax: $150.00

يسمح تعدد الأشكال باستدعاء التوابع بأنواع مُعامِلات مختلفة. العديد من العوامل والدوال المُضمَّنة لديها هذه الفائدة.

مثال 13.3

تعدد الأشكال والدوال

يمكن استدعاء len() بأنواع متعددة، بما في ذلك القوائم والسلاسل النصية.

    tree_list = ["ash", "hazel", "oak", "yew"]
    tree_1 = tree_list[0]
    print(len(tree_list))
    print(len(tree_1))

إخراج الشِفرة هو:

    4
    3

مفاهيم تطبيقية

تعدد الأشكال في الممارسة

8.
تأمل المثال. ما هو الإخراج؟
class Polygon:
  def print_type(self):
    print("Polygon type")

class Rectangle(Polygon):
  def print_type(self):
    print("Rectangle type")

class Square(Rectangle):
  def print_type(self):
    print("Square type")

sq_1 = Square()
sq_1.print_type()
  1. Polygon type
  2. Rectangle type
  3. Square type
9.
أي مما يلي يُعد مثالاً على تعدد الأشكال لعامل الضرب؟
  1. x = 3 * 4
    y = 3 * "la"
    
  2. x = 3 * 4
    y = str(x)
    
  3. x = 3 * 4
    y = x * x
    

جرّب بنفسك

إعادة تعريف التوابع

بفرض وجود الصنف Pet، أنشئ صنف Bird موروثًا من Pet وصنف Finch موروثًا من Bird. أعد تعريف التابع display() في Bird و Finch بحيث يكون إخراج البرنامج:

    Pet type: Bird
    Bird type: Finch

جرّب بنفسك

استخدام super()

بفرض وجود الصنف Employee، أنشئ صنف Developer موروثًا من Employee مع التابعين __init__() و print_info() بحيث:

__init__()

  • يستخدم super() لاستدعاء التابع __init__() الخاص بـ Employee لتهيئة e_id و hire_year.
  • يُسند lang_xp باستخدام مُعامِل القائمة lang_xp.

print_info()

  • يستخدم super() لطباعة e_id و hire_year.
  • يطبع "Language(s):" متبوعًا بـ lang_xp.