چند ریختی در پایتون – توضیح پلی مورفیسم به زبان ساده

۶۳۶ بازدید
آخرین به‌روزرسانی: ۰۳ بهمن ۱۴۰۲
زمان مطالعه: ۱۲ دقیقه
چند ریختی در پایتون – توضیح پلی مورفیسم به زبان ساده

«برنامه‌نویسی شی‌گرایانه» (Object-Oriented Programming | OOP) دارای ۴ ویژگی ضروری است. «مفاهیم انتزاعی» (Abstraction)، «کپسوله‌سازی» (Encapsulation)، «وراثت» (Inheritance)، و «چند ریختی» (Polymorphism) اصول چهارگانه‌ای هستند که شی‌گرایی بدون آن‌ها نه فایده‌ای دارد نه قابلیت پیاده‌سازی به شکل حرفه‌ای پیدا می‌کند. مجله فرادرس در این مطلب یکی از این اصول چهارگانه به نام چند ریختی را بررسی کرده است. در این مطلب از مجله فرادرس به چند ریختی در پایتون خواهیم پرداخت، اینکه چیست، چه جزییاتی دارد و چگونه پیاده‌سازی می‌شود و به‌طور کلی تلاش می‌کنیم نکته‌ای ناگفته‌ از مطلب چند ریختی باقی‌نماند. اما به عبارت کوتاه، چند ریختی، توانایی بر عهده گرفتن چند شکل مختلف را توسط متدها یا عملگرها تعریف می‌کند. چند ریختی در پایتون به ‌ما اجازه می‌دهد، متدی را در کلاس فرزند تعریف کنیم، درحالی که در کلاس والد نیز متدی با همان نام وجود دارد.

997696

چند ریختی چیست؟

کلمه چند ریختی، Polymorphism از دو کلمه یونانی Poly به معنی «چَند» و Morphism به معنی «شکل‌ها» گرفته شده است. در دنیای زبان‌های برنامه‌نویسی این کلمه به این معنا است که از یک تابع برای انواع مختلف داده می‌توان استفاده کرد. این روش، کد نویسی را بیشتر شهودی می‌کند و همین‌طور باعث آسانتر شدن طراحی کدها هم می‌شود.

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

چند ریختی در پایتون

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

 تصویر ساده و حداقلی که به صورت بصری مفهوم چندریختی در پایتون را توضیح می‌دهد. شامل نمایش اشیاء مختلف با رفتار چندریختی.

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

چند ریختی در تابع توکار len

«تابع توکار» (Built-In Function) len() طول اشیا را بسته به نوع آن‌ها محاسبه می‌کند. اگر شیئی از نوع رشته باشد، تابع len()تعداد کارکترها را برمی‌گرداند و اگر شیئی از نوع لیست باشد تعداد آیتم‌های درون لیست را می‌شمارد و برمی‌گرداند.

تابع len()با هر شیئی بسته به نوع کلاس آن شی رفتار می‌کند.

نمونه کد

در مثالی که در نمونه کد زیر آورده‌ایم، روش کار این تابع نمایش داده شده است.

1students = ['Emma', 'Jessa', 'Kelly']
2school = 'ABC School'
3
4# calculate count
5print(len(students))
6print(len(school))

خروجی کد بالا به صورت زیر می‌شود.

3
10

توجه کنید که تابع len() در برخورد با نوع داده دیکشنری تعداد کلید‌ها را برمی‌گرداند که نمی‌توان گفت تعداد کل آیتم‌ها هستند. چون مقدار هر کلید در دیکشنری خود، می‌تواند یک دیکشنری دیگر باشد.

چند ریختی با وراثت

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

با استفاده از تکنیک «بازنویسی متد» (Method Overriding)، چند ریختی به ما اجازه می‌دهد که متدهایی را در کلاس فرزند تعریف کنیم که هم‌نام متدهایی هستند که در کلاس والد وجود دارند. به این فرایند پیاده‌سازی دوباره متدهایی که از کلاس والد به کلاس فرزند به ارث رسیده‌اند، بازنویسی متد یا همان Method Overriding می‌گویند.

انواع کاربردهای پایتون به دور نماد پایتون حلقه زده اند.

مزایای قابلیت بازنویسی متد

دو امتیاز مهم را این قابلیت در اختیار برنامه نویسان قرار می‌دهد.

  • زمانی که با ایجاد تغییر در متدهای به ارث رسیده، می‌خواهیم عملکرد متدها را گسترش دهیم، این قابلیت بسیار مفید و موثر است یا وقتی متدی که از کلاس والد به ارث رسیده‌ است نیازهای کلاس فرزند را برآورده نمی‌کند، قادر به پیاده‌سازی دوباره همان متد اما به روش متفاوتی، در کلاس فرزند هستیم.
  • هنگامی که کلاس والد چندین زیرکلاس دارد و یکی از آن کلاس‌های فرزند می‌‌خواهد که متد را بازنویسی کند. کلاس‌های فرزند دیگر می‌توانند از متدی که از کلاس والد به ارث برده‌اند، بدون هیچ تغییر و مشکلی استفاده کنند. باتوجه به این، دیگر نیازی به اعمال تغییر در کدهای کلاس والد وجود ندارد.

در چند ریختی، وقتی که متدی را فراخوانی می‌کنیم، پایتون اول نوع کلاس اشیا را بررسی می‌کند و سپس متد مناسب با آن نوع کلاس را اجرا می‌کند. برای مثال، اگر کلاس خودرو را ایجاد کنید و برای این کلاس متد سرعتspeed() را تعریف کنید سپس از این کلاس دو کلاس فرزند به نام‌های ماشین کشاورزی و ماشین مسابقه ارث‌بری کنند. وقتی که از کلاس ماشین مسابقه شیئی ایجاد کنید و تابع speed() را فراخوانی کنید، درنتیجه پایتون نیز متد speed() را از کلاس ماشین مسابقه فراخوانی می‌کند نه متد speed() مربوط به کلاس خودرو یا کلاس ماشین کشاورزی.

در ادامه به روش کار قابلیت بازنویسی متدها با استفاده از مثال خواهیم پرداخت.

مثال به همراه کد برای بازنویسی متد

در مثالی که آورده‌ایم، کلاس وسیله نقلیه به نام «Vehicle» را به عنوان والد و کلاس ماشین سواری و کامیون را به ترتیب به نام‌های «Car» و «Truck» به عنوان کلاس‌های فرزند تعریف کرده‌ایم. اما هر وسیله نقلیه‌ای می‌تواند ظرفیت مسافر، سرعت و وزن و غیره متفاوتی مخصوص به خود داشته باشد. درنتیجه می‌توانیم چند نمونه متد یکسان با نام یکسان در هر کلاس داشته باشیم که هرکدام پیاده‌سازی مخصوص به‌ خود را دارند. کدهایی که طبق این روش نوشته شوند می‌توانند در طول زمان گسترش‌داده شوند و به‌ آسانی نگهداری شوند.

مثالی برای درک چند ریختی در پایتون
متدهای بازنویسی شده در کلاس‌های خودرو سواری «Car» و کامیون «Truck»
1class Vehicle:
2
3    def __init__(self, name, color, price):
4        self.name = name
5        self.color = color
6        self.price = price
7
8    def show(self):
9        print('Details:', self.name, self.color, self.price)
10
11    def max_speed(self):
12        print('Vehicle max speed is 150')
13
14    def change_gear(self):
15        print('Vehicle change 6 gear')
16
17
18# inherit from vehicle class
19class Car(Vehicle):
20    def max_speed(self):
21        print('Car max speed is 240')
22
23    def change_gear(self):
24        print('Car change 7 gear')
25
26
27# Car Object
28car = Car('Car x1', 'Red', 20000)
29car.show()
30# calls methods from Car class
31car.max_speed()
32car.change_gear()
33
34# Vehicle Object
35vehicle = Vehicle('Truck x1', 'white', 75000)
36vehicle.show()
37# calls method from a Vehicle class
38vehicle.max_speed()
39vehicle.change_gear()

خروجی این کد به صورت زیر می‌شود.

Details: Car x1 Red 20000
Car max speed is 240
Car change 7 gear

Details: Truck x1 white 75000
Vehicle max speed is 150
Vehicle change 6 gear

همان‌طور که می‌توانید ببینید، بر طبق قابلیت چند ریختی، مفسر پایتون متوجه می‌شود که متدهایmax_speed() وchange_gear() برای شی خودرو بازنویسی شده‌اند. پس، از متد تعریف شده‌ در کلاس فرزند Car استفاده می‌کند. از طرف دیگر، متدshow() در کلاس فرزند Carبازنویسی نشده‌ است، پس این متد از کلاس Vehicle اجرا می‌شود.

در پایتون می‌ توانیم رفتارهای پیش فرض توابع توکار را نیز تغییر دهیم. برای مثال توابع توکاری مثل len() ، abs() وdivmod() را هم می‌توانیم گسترش دهیم و هم می‌توان رفتارشان را در کلاس خودمان تغییر دهیم. این کار را با دوباره تعریف کردن این توابع انجام می‌دهیم.

برای مشاهده نمونه‌ای از بازتعریف توابع توکار به کد زیر دقت کنید. در این مثال تابع len() را بازتعریف کرده‌ایم.

1class Shopping:
2    def __init__(self, basket, buyer):
3        self.basket = list(basket)
4        self.buyer = buyer
5
6    def __len__(self):
7        print('Redefine length')
8        count = len(self.basket)
9        # count total items in a different way
10        # pair of shoes and shir+pant
11        return count * 2
12
13shopping = Shopping(['Shoes', 'dress'], 'Jessa')
14print(len(shopping)

خروجی کد بالا به صورت زیر می‌شود.

Redefine length
4

چند ریختی در متدهای کلاسی

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

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

  1. ابتدا می‌خواهیم کلاس متفاوتی به همان روش با اضافه کردن متدهای یکسان در چندین کلاس طراحی کنیم.
  2. سپس از هر کلاس شیئی ایجاد خواهیم کرد.
  3. سپس همه اشیا را به تاپلی اضافه خواهیم کرد.
  4. در پایان، با استفاده از حلقه for  روی این تاپل پیمایش خواهیم کرد و متدهای اشیا را بدون بررسی کردن کلاس آنها فراخوانی خواهیم کرد.

نمونه کد برای چند ریختی در متدهای کلاسی

در مثالی که در زیر آورده‌ایم، توابع fuel_type() و max_speed() متدهای نمونه ایجاد شده در هر دو کلاس هستند.

1class Ferrari:
2    def fuel_type(self):
3        print("Petrol")
4
5    def max_speed(self):
6        print("Max speed 350")
7
8class BMW:
9    def fuel_type(self):
10        print("Diesel")
11
12    def max_speed(self):
13        print("Max speed is 240")
14
15ferrari = Ferrari()
16bmw = BMW()
17
18# iterate objects of same type
19for car in (ferrari, bmw):
20    # call methods without checking class of object
21    car.fuel_type()
22    car.max_speed()

خروجی کد بالا به این صورت می‌شود.

Petrol
Max speed 350

Diesel
Max speed is 240

همانطور که می‌بینید، دو کلاس فراریFerrari  و بی ام وBMW را ایجاد کرده‌ایم. این کلاس‌ها متدهای یکسانی را به عنوان نمونه برای تست، به نام‌های max_speed() و fuel_type() دارند. اگرچه، نه این دو کلاس را به هم مربوط کرده‌ایم و نه از وراثت استفاده کرده‌ایم.

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

چندریختی درباره توابع و اشیا

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

به مثالی که در ادامه زده‌ایم دقت کنید.

1class Ferrari:
2    def fuel_type(self):
3        print("Petrol")
4
5    def max_speed(self):
6        print("Max speed 350")
7
8class BMW:
9    def fuel_type(self):
10        print("Diesel")
11
12    def max_speed(self):
13        print("Max speed is 240")
14
15# normal function
16def car_details(obj):
17    obj.fuel_type()
18    obj.max_speed()
19
20ferrari = Ferrari()
21bmw = BMW()
22
23car_details(ferrari)
24car_details(bmw)

خروجی کدهایی که در بالا آورده‌ایم، به این شکل می‌شود.

Petrol
Max speed 350
Diesel
Max speed is 240

چند ریختی در متدهای توکار

تابع «توکار» (Built-In) reversed(obj) متغیرهای پیمایش‌پذیر را می‌پذیرد و محتویات درون آن‌ها را برعکس می‌کند و برمی‌گرداند. برای مثال اگر رشته‌ای را به آن بدهید، از آخر به اول آن را بصورت برعکس در می‌آورد. اما اگر لیستی از رشته‌ها را به آن تابع بدهید، آن لیست –شی پیمایش‌پذیر- را با معکوس کردن چیدمان عناصر داخل آن به شما برمی‌گرداند. این بار دیگر رشته‌ها را به‌صورت مجزا معکوس نخواهد کرد.

در زیر خواهیم دید که یک متد توکار چگونه اشیایی را که نوع داده‌های گوناگونی دارند، پردازش می کنند.

1students = ['Emma', 'Jessa', 'Kelly']
2school = 'ABC School'
3
4print('Reverse string')
5for i in reversed('PYnative'):
6    print(i, end=' ')
7
8print('\nReverse list')
9for i in reversed(['Emma', 'Jessa', 'Kelly']):
10    print(i, end=' ')

خروجی این کد به این شکل می‌شود.

Reverse string
e v i t a n Y P 

Reverse list
Kelly Jessa Emma 

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

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

  • پیاده‌سازی چندریختی به روش Duck Typing
  • پیاده‌سازی چندریختی به روش «سربارگزاری متد» (Method Overloading)
  • پیاده‌سازی چندریختی به روش «سربارگزاری عملگر» (Operator Overloading)
  • پیاده‌سازی چندریختی به روش «بازنویسی متد» (Method Overriding)

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

Duck Typing در پایتون چیست؟

روش Duck Typing مفهومی است که بیان می‌کند، نوع شی فقط در زمان اجرای برنامه اهمیت پیدا می‌کند و نیازی نیست که قبل از اجرای هر عملیاتی روی شی، نوع آن را یادآوری کنیم. همینکه کاری که میخواهیم انجام دهد، کافیست. به اصطلاح برنامه‌نویسان «اگر مانند یک اردک، کووَک کووَک کند، پس اردک است.»

مهندسان در حال پیاده سازی دنیای بهم متصل از طریق شبکه‌هایی از اطلاعات هستند.

در پایتون مفهومی به نام «نوع‌دهی پویا» (Dynamic Typing) وجود دارد. می‌توانیم نوع متغیر یا شی را بعدا مورد نظر قرار دهیم. ایده پشت کار این است که نیازی به دانستن نوع متد برای فراخوانی متد موجود داخل اشیا ندارید، اگر متدی برای شیئی تعریف شده، کافی است. می‌توانید آن متد را فراخوانی کنید.

لطفا به مثال زیر توجه کنید.

1class GFGCoder:
2	def execute(self):
3		print("Geeks are working on the code...")
4
5class Geeks:
6	def code(self, executor):
7		executor.execute()
8
9# Create instances of the classes
10executor = GFGCoder()
11ide = Geeks()
12
13# Call the code method using the IDE instance
14ide.code(executor)

خروجی کد بالا به صورت زیر می‌باشد.

Geeks are working on the code...

شاید کد بالا کمی گمراه‌کننده باشد. توجه کنید که در ابتدا دو کلاس GFGCoder و Geeks تعریف شده‌اند. سپس از هر دو کلاس اشیای به ترتیب executor و ide ساخته شده‌اند. شیide متدcode() را فراخوانی کرده و شیexecutor را به عنوان ورودی به آن متد ارسال کرده‌ است.

سربارگزاری متدها

به فرایند فراخوانی متدهای دارای نام یکسان که پارامترهای متفاوتی می‌پذیرند «سربارگزاری متد» (Method Overloading) می‌گویند. زبان برنامه‌نویسی پایتون فن‌آوری سربازگزاری متد را پشتیبانی نمی‌کند. پایتون فقط آخرین متدی که تعریف شده است را درنظر می‌گیرد. حتی اگر متد را «بازنویسی» (Overloading) کرده باشید. اگر سعی در سربارگزاری متدها کنید، پایتون خطای TypeError را به شما برمی‌گرداند.

برای مثال به کدی که در ادامه آمده، توجه کنید.

1def addition(a, b):
2    c = a + b
3    print(c)
4
5
6def addition(a, b, c):
7    d = a + b + c
8    print(d)
9
10
11# the below line shows an error
12# addition(4, 5)
13
14# This line will call the second product method
15addition(3, 7, 5)

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

برای مثال، تابع توکار range() ،۳ پارامتر مختلف را می‌پذیرد و با توجه به تعداد پارامترهایی که به تابع ارسال می‌کنیم، نتایج مختلفی را ارائه می‌دهد. در واقع به این تابع به ۳ روش گوناگون می‌توان پارامتر ارسال کرد که هر روش ارسال باعث می‌شود تابع range()رفتار متفاوتی نسبت به پارامترها داشته باشد.

برای نمونه به کدی که در زیر آمده، توجه کنید.

1for i in range(5): print(i, end=', ')
2print()
3for i in range(5, 10): print(i, end=', ')
4print()
5for i in range(2, 12, 2): print(i, end=', ')

خروجی کد بالا به صورت زیر می‌شود.

0, 1, 2, 3, 4, 
5, 6, 7, 8, 9, 
2, 4, 6, 8, 10,

فرض میکنیم که متدی به نام area() داریم که کارش محاسبه مساحت مربع و مستطیل است. این متد با توجه به تعداد پارامترهایی که به عنوان ورودی به آن ارسال می‌شود، مساحت را محاسبه می‌کند.

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

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

1class Shape:
2    # function with two default parameters
3    def area(self, a, b=0):
4        if b > 0:
5            print('Area of Rectangle is:', a * b)
6        else:
7            print('Area of Square is:', a ** 2)
8
9square = Shape()
10square.area(5)
11
12rectangle = Shape()
13rectangle.area(5, 3)

خروجی مثال بالا به شکل زیر خواهد بود.

Area of Square is: 25
Area of Rectangle is: 15

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

«سربارگزاری عملگر» (Operator Overloading) به معنی این است که بسته به عملوندهایی که استفاده می‌کنیم، رفتار پیش‌فرض عملگر مورد استفاده تغییر کند. به عبارت دیگر، می‌توانیم از عملگری یکسان برای هدف‌های مختلف استفاده کنیم.

برای مثال، عملگر + وقتی که همراه با اعداد استفاده می‌شود، عمل جمع ریاضی را انجام خواهد داد. به همین ترتیب وقتی که با عملوندهایی از جنس رشته مورد استفاده قرار گیرد باید عمل الحاق یا چسباندن رشته‌ها را به هم انجام د‌هد.

ماسک مجازی تراش خورده در پس زمینه خالی و ساده

عملگر + برای انجام عملیات مختلف روی نوع داده‌های مجزا از هم، مورد استفاده قرار می‌گیرد. این مورد یکی از ساده‌ترین مظاهر چند ریختی در پایتون است.

برای درک بهتر مطلبی که در بالا مورد اشاره قرار گرفت، لطفا به کد زیر توجه کنید.

1# add 2 numbers
2print(100 + 200)
3
4# concatenate two strings
5print('Jess' + 'Roy')
6
7# merger two list
8print([10, 20, 30] + ['jessa', 'emma', 'kelly'])

خروجی این کد به صورت زیر می‌شود.

300
JessRoy
[10, 20, 30, 'jessa', 'emma', 'kelly']

سربارگزاری عملگر + برای اشیا سفارشی کاربر

فرض کنید که دو شی داریم که توسط کلاسی که خود تعریف کرده‌ایم ایجاد شده‌اند. اکنون می‌خواهیم که این دو شی را به کمک عملگر باینری + با یکدیگر جمع ببندیم. البته توجه دارید که اگر این عملیات جمع را انجام دهیم، با خطا روبه‌رو خواهیم شد زیرا کامپایلر پایتون دو شی سفارشی را با هم جمع نمی‌کند. مثالی که در ادامه زده‌ایم را برای بررسی جزییات بیشتر ببینید.

1class Book:
2    def __init__(self, pages):
3        self.pages = pages
4
5# creating two objects
6b1 = Book(400)
7b2 = Book(300)
8
9# add two objects
10print(b1 + b2)

خروجی کد بالا به صورت زیر خواهد بود.

TypeError: unsupported operand type(s) for +: 'Book' and 'Book'

اگرچه که می‌توانیم عملگر+  را برای کار کردن با اشیا سفارشی Overload کنیم. پایتون چندین تابع مخصوص یا به اصطلاح جادویی را فراهم می‌کند که وقتی با عملگرهای اختصای روبه‌رو می‌شوند، به‌طورخودکار فراخوانی می‌شوند. برای مثال، زمانی که از عملگر+  استفاده می‌کنیم، متد جادویی __add__() به صورت خودکار فراخوانی می‌شود. عملگر داخلی +  با استفاده از متد __add__() پیاده‌سازی شده است. برای استفاده از عملگر + به منظور جمع کردن اشیا سفارشی، مجبوریم که این متد را داخل کلاس خودمان بازنویسی کنیم.

به مثال زیر توجه کنید.

1class Book:
2    def __init__(self, pages):
3        self.pages = pages
4
5    # Overloading + operator with magic method
6    def __add__(self, other):
7        return self.pages + other.pages
8
9b1 = Book(400)
10b2 = Book(300)
11print("Total number of pages: ", b1 + b2)

خروجی کد بالا به‌صورت زیر خواهد بود.

Total number of pages: 700

سربارگزاری عملگر *

عملگر* برای اجرای عملیات ضرب مورد استفاده قرار می‌گیرد. برای نمونه، روش Overload کردن این عملگر را برای محاسبه میزان حقوق کارمندان در دوره‌های زمانی خاصی پیاده‌سازی خواهیم کرد. عملگر داخلی * با استفاده از متد__mul__() پیاده‌سازی شده است.

لطفا به پیاده‌سازی زیر دربار این متد توجه کنید.

1class Employee:
2    def __init__(self, name, salary):
3        self.name = name
4        self.salary = salary
5
6    def __mul__(self, timesheet):
7        print('Worked for', timesheet.days, 'days')
8        # calculate salary
9        return self.salary * timesheet.days
10
11
12class TimeSheet:
13    def __init__(self, name, days):
14        self.name = name
15        self.days = days
16
17
18emp = Employee("Jessa", 800)
19timesheet = TimeSheet("Jessa", 50)
20print("salary is: ", emp * timesheet)

خروجی این کد به صورت زیر خواهد بود.

Wroked for 50 days
salary is:  40000

متدهای جادویی

«متدهای جادویی» (Magic Methods) در پایتون به متدهای خاصی گفته می‌شود که در هر طرف نامشان دو «خط زیر» (Underscore) وجود دارد. این متدها به‌صورت توکار برای پایتون طراحی شده‌اند و عمده مصرف این‌ها برای پیاده‌سازی عملیات «سربارگزاری» (Overloading) در پایتون است. به منظور این کار متدهای جادویی مختلفی وجود دارند. نام‌های متدهای جادویی که برای سربارگزاری عملگرهای ریاضی، عملگرهای انتسابی و عملگرهای رابطه‌ای استفاده ‌می‌شوند را در جدول‌هایی در زیر آورده‌ایم.

در ابتدا با «عملگرهای ریاضی» (Mathematical Operator) یا عملگرهای محاسبه‌ای شروع می‌کنیم.

نام عملگرنمادمتد جادویی
جمع+__add__(self, other)
منها-__sub__(self, other)
ضرب*__mul__(self, other)
تقسیم/__div__(self, other)
تقسیم صحیح//__floordiv__(self,other)
باقیمانده٪__mod__(self, other)
توان**__pow__(self, other)

جدول بعدی متعلق به «عملگرهای انتسابی» (Assignment Operator) است.

نام عملگرنمادمتد جادویی
«انتساب افزایشی» (Increment)+=__iadd__(self, other)
«انتساب کاهشی» (Decrement)-=__isub__(self, other)
«انتساب ضربی» (Product)*=__imul__(self, other)
تقسیم/=__idiv__(self, other)
باقیمانده%=__imod__(self, other)
توان**=__ipow__(self, other)

و جدول آخر به «عملگرهای رابطه‌ای» (Relational Operators) تعلق دارد.

نام عملگرنمادمتد جادویی
کوچکتر<__lt__(self, other)
برزگتر>__gt__(self, other)
کوچکتر یا مساوی<=__le__(self, other)
بزرگتر یا مساوی>=__ge__(self, other)
مساوی بودن یا نه | برابری==__eq__(self, other)
نا‌مساوی بودن یا نه | نابرابری!=__ne__(self, other)

جمع بندی

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

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

بر اساس رای ۲ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
GeeksforGeeksedurekaeducativepynative
۱ دیدگاه برای «چند ریختی در پایتون – توضیح پلی مورفیسم به زبان ساده»

بسیار عالی و جامع بود.

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *