آموزش پایتون - چندریختی

چندریختی (Polymorphism) در پایتون چیست؟

اصطلاح چندریختی به حالتی اشاره دارد که یک تابع یا متد در زمینه‌های مختلف، رفتارهای متفاوتی از خود نشان می‌دهد. از آنجا که پایتون یک زبان با نوع‌دهی پویا است، پیاده‌سازی چندریختی در پایتون بسیار آسان است.

اگر متدی در یک کلاس والد، در کلاس‌های فرزند مختلف با منطق کسب‌وکار متفاوت بازنویسی شود، متد کلاس پایه یک متد چندریختی محسوب می‌شود.

روش‌های پیاده‌سازی چندریختی در پایتون

چهار روش برای پیاده‌سازی چندریختی در پایتون وجود دارد:

  • تایپ اردکی (Duck Typing)
  • سربارگذاری عملگرها (Operator Overloading)
  • بازنویسی متد (Method Overriding)
  • سربارگذاری متد (Method Overloading)

implementing polymorphism

تایپ اردکی در پایتون

تایپ اردکی مفهومی است که در آن، نوع یا کلاس یک شیء اهمیت کمتری نسبت به متدهایی دارد که تعریف می‌کند. با استفاده از این مفهوم، می‌توانید هر متدی را روی یک شیء فراخوانی کنید، بدون اینکه نوع آن را بررسی کنید؛ به شرطی که آن متد وجود داشته باشد.

این اصطلاح با نقل‌قول بسیار مشهوری تعریف می‌شود که می‌گوید: فرض کنید پرنده‌ای وجود دارد که مانند اردک راه می‌رود، مانند اردک شنا می‌کند، شبیه اردک به نظر می‌رسد و مانند اردک صدا درمی‌آورد؛ در این صورت، احتمالاً آن پرنده یک اردک است.

مثال

در کد زیر، مفهوم تایپ اردکی را به‌صورت عملی نشان می‌دهیم.

class Duck:
   def sound(self):
      return "Quack, quack!"

class AnotherBird:
   def sound(self):
      return "I'm similar to a duck!"

def makeSound(duck):
   print(duck.sound())

# creating instances
duck = Duck()
anotherBird = AnotherBird()
# calling methods
makeSound(duck)   
makeSound(anotherBird) 

با اجرای این کد، خروجی زیر تولید می‌شود:

Quack, quack!
I'm similar to a duck!

بازنویسی متد در پایتون

در بازنویسی متد، متدی که درون یک زیرکلاس تعریف شده است، نامی یکسان با متدی در ابرکلاس خود دارد، اما عملکرد متفاوتی را پیاده‌سازی می‌کند.

مثال

در مثال زیر برای نشان دادن چندریختی، کلاس shape را داریم که یک کلاس انتزاعی است. این کلاس به‌عنوان والد دو کلاس circle و rectangle استفاده می‌شود. هر دو کلاس، متد draw() کلاس والد را به روش‌های متفاوتی بازنویسی می‌کنند.

from abc import ABC, abstractmethod
class shape(ABC):
   @abstractmethod
   def draw(self):
      "Abstract method"
      return

class circle(shape):
   def draw(self):
      super().draw()
      print ("Draw a circle")
      return

class rectangle(shape):
   def draw(self):
      super().draw()
      print ("Draw a rectangle")
      return

shapes = [circle(), rectangle()]
for shp in shapes:
   shp.draw()

خروجی

با اجرای کد بالا، خروجی زیر تولید می‌شود:

Draw a circle
Draw a rectangle

متغیر shp ابتدا به شیء circle اشاره می‌کند و متد draw() را از کلاس circle فراخوانی می‌کند. در تکرار بعدی، این متغیر به شیء rectangle اشاره می‌کند و متد draw() را از کلاس rectangle فراخوانی می‌کند. بنابراین، متد draw() در کلاس shape چندریختی است.

سربارگذاری عملگرها در پایتون

فرض کنید کلاسی به نام Vector برای نمایش بردارهای دوبعدی ایجاد کرده‌اید. وقتی از عملگر جمع برای جمع کردن آن‌ها استفاده می‌کنید، چه اتفاقی می‌افتد؟ به احتمال زیاد پایتون خطا می‌دهد.

با این حال، می‌توانید متد __add__ را در کلاس خود تعریف کنید تا جمع بردارها را انجام دهد. در این صورت، عملگر جمع مطابق انتظار شما عمل خواهد کرد:

مثال

class Vector:
   def __init__(self, a, b):
      self.a = a
      self.b = b

   def __str__(self):
      return 'Vector (%d, %d)' % (self.a, self.b)

   def __add__(self,other):
      return Vector(self.a + other.a, self.b + other.b)

v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)

با اجرای کد بالا، نتیجه زیر تولید می‌شود:

Vector(7,8)

سربارگذاری متد در پایتون

وقتی یک کلاس شامل دو یا چند متد با نام یکسان، اما تعداد پارامترهای متفاوت باشد، این وضعیت را می‌توان سربارگذاری متد نامید.

پایتون به‌صورت پیش‌فرض اجازه سربارگذاری متدها را نمی‌دهد؛ با این حال، می‌توانیم با استفاده از روش‌هایی مانند فهرست آرگومان‌های با طول متغیر، ارسال چندگانه (Multiple Dispatch) و پارامترهای پیش‌فرض، این قابلیت را پیاده‌سازی کنیم.

مثال

در مثال زیر، برای پیاده‌سازی سربارگذاری متد از فهرست آرگومان‌های با طول متغیر استفاده می‌کنیم.

def add(*nums):
   return sum(nums)

# Call the function with different number of parameters
result1 = add(10, 25)
result2 = add(10, 25, 35)

print(result1)  
print(result2) 

با اجرای کد بالا، نتیجه زیر تولید می‌شود:

35
70

پیشرفت مطالعه: وضعیت خوانده‌شدن این درس فقط در همین مرورگر ذخیره می‌شود؛ بدون نیاز به حساب کاربری.