الدالة super()‎ في بايثون

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث

تعيد الدّالة super()‎ كائنًا وسيطًا يُفوّض استدعاءات التّوابع إلى صنف أبٍ أو صنف شقيق للصّنف الذي استُدعيَت منه الدّالة.

هذا مُفيد للوصول إلى التّوابع الموروثة التي أعيدت كتابتها في صنف مُعيّن. ترتيب البحث يكون هو نفسه التّرتيب المُستخدم من طرف الدّالة getattr()‎ لكنّ النّوع type‎ المُعطى يُتجاهَل.

البنية العامة

super([type[, object-or-type]])

المعاملات

type

النّوع الذي ستقوم الدّالة super()‎ بتفويض استدعاءات التّوابع إلى صنف أبٍ أو صنف شقيق له.

تعرض الخاصيّة __mro__ الخاصّة بالصّنف type ترتيب البحث عن التوابع (method resolution search order) الذي يُستعمل من طرف كلّ من الدّالة getattr‎()‎ والدّالة super()‎ . وتكون الخاصيّة ديناميكيّةً يُمكن لها أن تتغيّر كلّمَا حُدّثت شجرة الوراثة (inheritance hierarchy).

object-or-type

إن لم تُمرّر قيمة للمُعامل، فالكائن super‎ المُعاد لا يكون مربوطًا (unbound). إن كانت قيمة المُعامل كائنًا، فيجب على الاستدعاء ‎isinstance‎(‎obj‎,‎ type‎)‎‎ أن يُعيد القيمة True‎. إن كانت قيمة المُعامل نوعًا (type)، فيجب على الاستدعاء ‎issubclass‎(‎type2‎,‎ type‎)‎‎ أن يُعيد القيمة True‎ (وهذا مُفيد لتوابع الأصناف).

القيمة المعادة

كائن وسيط يُفوّض استدعاءات التّوابع إلى صنف أبٍ أو صنف شقيق للصّنف الذي استُدعيَت منه الدّالة.

أمثلة

هناك حالتان تُستعمل فيهما الدّالة super()‎ عادةً:

  • تُستعمَل للوصول إلى الصّنف الأب في شجرة أصناف ذات وراثة وحيدة (class hierarchy with single inheritance) دون الحاجة إلى تسميّة الصّنف مُباشرة، ما يسمح للشّيفرة بأن تكون قابلة للتّطوير والصّيانة بشكل أفضل، وحالة الاستخدام هذه تُشابه طريقة استخدام super()‎ في لغات البرمجة الأخرى.

المثال التّالي يُوضّح حالة الاستخدام هذه:

>>> class A:
...     def add(self, x, y): # دالّة تُعيد مجموع عددين
...         return x+y
...     
>>> class B(A): # وراثة من الصّنف الأب
...     def add_print(self, x, y): # دالّة تُضيف العددين وتطبع العمليّة
...         result = super().add(x, y) # استدعاء تابع يتواجد في الصّنف الأب دون تسميّة الصّنف الأب صراحةً
...         print(f'{x}+{y}={result}') # طباعة العمليّة الحسابيّة
...         
>>> adder = B() # إنشاء كائن من الصّنف الوارث
>>> adder.add_print(1, 2) # استدعاء دالّة الإضافة والطّباعة
1+2=3
>>> adder.add_print(1, 5)
1+5=6
  • الحالة الثّانيّة التي تُستخدم فيها هي عند الرّغبة في استعمال وراثة مُتعدّدة مُتعاونة (cooperative multiple inheritance) في بيئة تنفيذ ديناميكيّة (أي أنّ الصّنف يُمكن له أن يرث من أكثر من صنف واحد). هذه الميّزة موجودة فقط في لغة بايثون ولا توجد في لغات البرمجة المُجمَّعَة (statically compiled languages) أو في لغات البرمجة التي لا تدعم سوى الوراثة الوحيدة (أي أنّ الصّنف لا يُمكن أن يرث إلّا من صنف واحد فقط). ما يُمكّن من تطبيق "تخطيطات الماسة (diamond diagrams)" عندما تحتوي أصناف آباء عدّة على نفس التّابع. يجب على هذا التّابع أن يمتلك نفس توقيع الاستدعاء (calling signature) في جميع الحالات (لأنّ ترتيب الاستدعاءات يُحدَّد في وقت التّنفيذ runtime، ولأنّ الترتيب يتغيّر حسب تغيّر شجرة الأصناف، ولأنّ التّرتيب يُمكن له أن يشمل أصنافًا شقيقةً غير معلومة قبل بدء وقت التّنفيذ).

يكون استدعاء الدّالة super()‎ في كلتا الحالتين مُشابهًا لما يلي:

class C(B):
    def method(self, arg):
        super().method(arg)    # هذا الاستدعاء مُكافئ للاستدعاء أدناه
                               # super(C, self).method(arg)

ملاحظات

  • الدّالة super()‎ مُطبَّقَة كجزء من عمليّة الرّبط للبحث عن الخاصيّات بوضوح (explicit) باستخدام النّقطة .‎ مثل ‎super‎(‎)‎.‎_‎_‎getitem‎_‎_‎(‎name‎)‎. وتقوم بذلك عبر تطبيق تابع ‎_‎_‎getattribute‎_‎_‎(‎)‎ خاصّ بها للبحث عن الأصناف بترتيب يُمكن توقّعه يَدعم الوراثة المُتعدّدة المُتعاونة. وبالتّالي، فالدّالة super‎()‎ غير مُعرّفَة للبحث الضّمني (implicit) باستخدام الجمل أو العوامل مثل super‎()‎‎[‎name‎]‎.
  • استخدام super‎()‎ ليس محصورًا داخل التّوابع فقط، المُعاملان اللذان تقبلهما الدّالة يُحدّدان المراجع المُناسبة التي ستُنشأ. استدعاء الدّالة دون عوامل يعمل داخل الأصناف فقط، لأنّ المُجمّع يملأ الفراغات المطلوبة للحصول على الصّنف المُناسب، إضافةً للوصول إلى النّسخة الحاليّة للتّوابع العاديّة.

انظر أيضًا

  • دليل استخدام الدّالة ‎super‎()‎. لأمثلة على كيفيّة تصميم أصناف تعاونيّة باستخدام الدّالة.
  • وراثة الأصناف في بايثون. لفهم أكثر لكيفيّة عمل الوراثة في بايثون.
  • الدالة isinstance()‎: تعيد قيمة منطقيّةً تُشير إلى ما إذا كان الكائن المُعطى نسخة (instance) من الصّنف المُعطى أم لا.
  • الدالة issubclass()‎: تعيد قيمة منطقيّةً تُشير إلى ما إذا كان الصّنف المُعطى صنفًا فرعيًّا لصنف آخر أم لا.
  • الدالة getattr()‎: تعيد قيمة الخاصيّة المُعطاة من الكائن المُعطى.

مصادر