دليل تعلم بايثون كيفية تطبيق التعددية الشكلية (Polymorphism) على الأصناف في بايثون 3


محمد الميداوي

التعددية الشكلية (Polymorphism) هي القدرة على استخدام واجهة موحدة لعدة أشكال مختلفة، مثل أنواع البيانات أو الأصناف . هذا يسمح للدوال باستخدام كيانات من أنواع مختلفة.

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

سوف تتعلم في هذا الدرس كيفية تطبيق التعددية الشكلية على أصناف بايثون.

كـيـفـيــــة-تطـبـيـــق-التعددية-الشكلية-Polymorphism-عـلـــى-الأصــنــــــاف.jpg

ما هي التعددية الشكلية؟

التعددية الشكلية هي إحدى السمات الأساسية للأصناف في بايثون، وتُستخدَم عندما تكون هناك توابع لها نفس الأسماء في عدة أصناف، أو أصناف فرعية. يسمح ذلك للدوال باستخدام كائنات من أيٍّ من تلك الأصناف والعمل عليها دون الاكتراث لنوعها.

يمكن تنفيذ التعددية الشكلية عبر [الوراثة](رابط المقالة السابقة)، أو باستخدام توابعِ الأصناف الفرعية، أو إعادة تعريفها (overriding).

يستخدم بايثون نظام أنواع (typing) خاص، يسمى «نظام التحقق من الأنواع: البطة نموذجًا» (Duck Typing)، وهو حالة خاصة من أنظمة التحقق من الأنواع الديناميكية (Dynamic Typing). يستخدم هذا النظامُ التعدُّديةَ الشكلية، بما في ذلك الربط المتأخر، والإيفاد الديناميكي. يعتمد هذا النظام على «نموذج البطة» بناءً على اقتباسٍ للكاتب جيمس ويتكومب رايلي:

اقتباس

"عندما أرى طائرًا يمشي مثل بطة، ويسبح مثل بطة، وصوته كصوت البطة، فسأعدُّ هذا الطير بطةً"

خُصِّص هذا المفهوم من قبل مهندس الحاسوب الإيطالي أليكس مارتيلي (Alex Martelli) في رسالة إلى مجموعة comp.lang.python، يقوم نظام التحقق من الأنواع هذا الذي يعتمد البطة نموذجًا على تعريف الكائن من منظور ملاءمة الغرض الذي أُنشِئ لأجله. عند استخدام نظام أنواع عادي، فإنّ ملاءمة الكائن لغرض مُعيَّن يتحدد بنوع الكائن فقط، ولكن في نموذج البطة، يَتحدَّد ذلك بوجود التوابع والخاصيات الضرورية لذلك الغرض بدلًا من النوع الحقيقي للكائن. بمعنى آخر، إذا أردت أن تعرف إن كان الكائن بطةً أم لا، فعليك التحقق مما إذا كان ذلك الكائن يمشي مشي البطة، وصوته كصوت البطة، بدلًا من أن تسأل عما إذا كان الكائن بطةً.

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

إنشاء أصناف متعددة الأشكال

للاستفادة من التَعدُّدية الشكلية، سننشئ صنفين مختلفين لاستخدامهما مع كائنين مختلفين. يحتاج هذان الصنفان المختلفان واجهة موحدة يمكن استخدامها بطريقة تعدُّدية الشكل (polymorphically)، لذلك سنعرّف فيهما توابع مختلفة، ولكن لها نفس الاسم.

سننشئ صنفًا باسم ‎Shark‎ وصنفُا آخر باسم ‎Clownfish‎، وسيُعرِّف كل منهما التوابع ‎swim()‎ و ‎swim_backwards()‎ و ‎skeleton()‎.

class Shark():
    def swim(self):
        print("القرش يسبح.")

    def swim_backwards(self):
        print("لا يمكن للقرش أن يسبح إلى الوراء، لكن يمكنه أن يغوص إلى الوراء.")

    def skeleton(self):
        print("هيكل القرش مصنوع من الغضروف.")


class Clownfish():
    def swim(self):
        print("سمكة المهرج تسبح.")

    def swim_backwards(self):
        print("يمكن لسمكة المهرج أن تسبح إلى الخلف.")

    def skeleton(self):
        print("هيكل سمكة المهرج مصنوع من العظام.")

في الشيفرة أعلاه، لدى الصنفين ‎Shark‎ و ‎Clownfish‎ ثلاث توابع تحمل نفس الاسم بيْد أنّ وظائف تلك التوابع تختلف من صنف لآخر.

دعنا نستنسخ (instantiate) من هذين الصنفين كائنين:

...
sammy = Shark()
sammy.skeleton()

casey = Clownfish()
casey.skeleton()

عند تنفيذ البرنامج باستخدام الأمر ‎python polymorphic_fish.py‎، يمكننا أن نرى أنّ كل كائن يتصرف كما هو متوقع:

هيكل القرش مصنوع من الغضروف.
هيكل سمكة المهرج مصنوع من العظام.

الآن وقد أصبح لدينا كائنان يستخدمان نفس الواجهة، فبمقدورنا استخدام هذين الكائنين بنفس الطريقة بغض النظر عن نوعيهما.

التعددية الشكلية في توابع الأصناف

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

...
sammy = Shark()

casey = Clownfish()

for fish in (sammy, casey):
    fish.swim()
    fish.swim_backwards()
    fish.skeleton()

لدينا كائنان، ‎sammy‎ من الصنف ‎Shark‎، و ‎casey‎ من الصنف ‎Clownfish‎. تمر حلقة ‎for‎ على هذين الكائنين، وتستدعي التوابع ‎swim()‎ و ‎swim_backwards()‎ و ‎skeleton()‎ على كل منها.

عند تنفيذ البرنامج، سنحصل على المخرجات التالية:

القرش يسبح.
لا يمكن للقرش أن يسبح إلى الوراء، لكن يمكنه أن يغوص إلى الوراء.
هيكل القرش مصنوع من الغضروف.
سمكة المهرج تسبح.
يمكن لسمكة المهرج أن تسبح إلى الخلف.
هيكل سمكة المهرج مصنوع من العظام.

مرت الحلقة ‎for‎ على الكائن ‎sammy‎ من الصنف ‎Shark‎، ثم على الكائن ‎casey‎ المنتمي إلى الصنف ‎Clownfish‎، لذلك نرى التوابع الخاصة بالصنف ‎Shark‎ قبل التوابع الخاصة بالصنف ‎Clownfish‎.

يدلُّ هذا على أنَّ بايثون تستخدم هذه التوابع دون أن تعرف أو تعبأ بتحديد نوع الصنف الخاص بالكائنات. وهذا مثال حي على استخدام التوابع بطريقة مُتعدِّدَة الأشكال.

التعددية الشكلية في الدوال

يمكننا أيضًا إنشاء دالة تقبل أيّ شيء، وهذا سيسمح باستخدام التعددية الشكلية.

لننشئ دالة تسمى ‎in_the_pacific()‎، والتي تأخذ كائنًا يمكننا تسميته ‎fish‎. رغم أننا سنستخدم الاسم ‎fish‎، إلا أنه يمكننا استدعاء أي كائن في هذه الدالة:


def in_the_pacific(fish):

بعد ذلك، سنجعل الدالة تستخدم الكائن ‎fish‎ الذي مرّرناه إليها. وفي هذه الحالة، سنستدعي التابع ‎swim()‎ المعرّف في كل من الصنفين ‎Shark‎ و ‎Clownfish‎:

...
def in_the_pacific(fish):
    fish.swim()

بعد ذلك، سننشئ نسخًا (instantiations) من الصنفين ‎Shark‎ و ‎Clownfish‎ لنمرّرهما بعد ذلك إلى نفس الدالة ‎in_the_pacific()‎:

...
def in_the_pacific(fish):
    fish.swim()

sammy = Shark()

casey = Clownfish()

in_the_pacific(sammy)
in_the_pacific(casey)

عند تنفيذ البرنامج، سنحصل على المخرجات التالية:

القرش يسبح.
سمكة المهرج تسبح.

رغم أننا مرّرنا كائنًا عشوائيًا (‎fish‎) إلى الدالة ‎in_the_pacific()‎ عند تعريفها، إلا أننا ما زلنا قادرين على استخدامها استخدامًا فعالًا، وتمرير نسخ من الصنفين ‎Shark‎ و ‎Clownfish‎ إليها. استدعى الكائنُ ‎casey‎ التابعَ ‎swim()‎ المُعرَّف في الصنف ‎Clownfish‎، فيما استدعى الكائنُ ‎sammy‎ التابعَ ‎swim()‎ المُعرَّف في الصنف ‎Shark‎.

خلاصة

تسمح التعدُّدية الشكلية باستخدام الكائنات بغض النظر عن نوعها، وهذا يوفر لبايثون مرونة كبيرة، وقابلية لتوسيع الشيفرة الكائنية.

هذه المقالة جزء من سلسة مقالات حول تعلم البرمجة في بايثون 3.

ترجمة -وبتصرّف- للمقال How To Apply Polymorphism to Classes in Python 3 لصاحبته Lisa Tagliaferri

اقرأ أيضًا





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن