شی گرایی در پایتون چیست؟ – به زبان ساده + مثال و تمرین

۶۳۸۶ بازدید
آخرین به‌روزرسانی: ۱۰ اردیبهشت ۱۴۰۳
زمان مطالعه: ۲۷ دقیقه
شی گرایی در پایتون چیست؟ – به زبان ساده + مثال و تمرین

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

فهرست مطالب این نوشته

شی گرایی در پایتون چیست ؟

رویکرد برنامه نویسی شی گرا، بر پایه دو مفهوم اصلی «شی» (Object) و «کلاس» (Class) شکل گرفته است که با این دو مفهوم می‌توان اطلاعات تمام «ماهیت‌های» (Entity) دنیای واقعی را در برنامه به راحتی ذخیره کرد.

به بیان جزئی‌تر می‌توان گفت هدف از شی گرایی در پایتون ساخت کلاسی است که دربرگیرنده تمامی «ویژگی‌ها» (Properties) و «رفتارها | متدهای» (Methods) مرتبط با یک ماهیت باشد و از طریق ایجاد شی از کلاس مربوطه بتوان به همه «مشخصه‌ها» (Attributes) یا همان ویژگی‌ها و متدها دسترسی داشت.

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

مثال شی گرایی در پایتون چیست

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

شی و کلاس در پایتون

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

حال فرض کنید قصد دارید با اطلاعات پیچیده‌تری در برنامه نویسی کار کنید. به عنوان مثال، مدیر شرکت از شما می‌خواهد اطلاعات شخصی کارمندان را در یک فایل ذخیره کنید. این اطلاعات می‌تواند شامل نام کارمندان، سن، سِمَت شغلی و تاریخ شروع به کار آن‌ها باشد.

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

1kirk = ["James Kirk", 34, "Captain", 2265]
2spock = ["Spock", 35, "Science Officer", 2254]
3mccoy = ["Leonard McCoy", "Chief Medical Officer", 2266]

با این که چنین روشی برای ذخیره اطلاعات کارمندان، ساده است، اما مشکلات مهمی دارد. چنانچه بخواهید به اطلاعات هر شخص در برنامه دسترسی داشته باشید، باید از اندیس استفاده شود. به عنوان مثال، اندیس صفر از لیست kirk  ، نام کارمند را مشخص می‌کند.

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

به علاوه، چنانچه اطلاعات کارمندان کامل نباشد، طول لیست‌های تعریف شده برای هر یک از کارمندان یکسان نخواهد بود و شماره اندیس‌های مشابه تمامی لیست‌ها، به اطلاعات یکسانی اشاره نخواهند داشت. در مثالی که ارائه کردیم، اندیس ۱ در لیست‌های kirk و spock به سن ۲ کارمند اشاره دارد، اما در لیست mccoy ، آیتمی برای سن کارمند لحاظ نشده است و اندیس ۱ از این لیست، عنوان شغلی کارمند را نشان می‌دهد.

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

مفاهیم کلاس و شی در شی گرایی پایتون

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

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

نحوه تعریف کلاس و شی در پایتون

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

به منظور تعریف کلاس در پایتون، از کلمه کلیدی class  استفاده می‌شود. پس از نوشتن این کلمه کلیدی، باید نام کلاس درج شود. برای نوشتن حرف اول نام کلاس باید از حروف بزرگ انگلیسی استفاده کنیم. در نهایت، علامت دو نقطه ( :  ) پس از نام کلاس قرار می‌گیرد. در ادامه، مثالی از نحوه ساخت کلاس برای کلاس Person ملاحظه می‌شود.

1class Person:
2    pass

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

از کلمه کلیدی pass  در توابع و کلاس‌هایی استفاده می‌شود که دستورات در بدنه آن‌ها نوشته نشده باشند. نوشتن این کلمه کلیدی باعث می‌شود در زمان اجرای برنامه، خطای کامپایلر رخ ندهد. پس از ساخت کلاس، می‌توان شیئی را به عنوان وهله‌ای از کلاس ساخت. در بخش پیشین، از متغیری با عنوان kirk برای ذخیره اطلاعات یکی از کارمندان استفاده کردیم. از همین نام برای ساخت یک شی جدید از کلاس Person به روش زیر استفاده می‌کنیم:

1class Person:
2    pass
3
4kirk = Person()

چنانچه کلاس Person دارای ویژگی‌ها و متدهای مختلف باشد، با استفاده از شی kirk می‌توان به هر یک از آن‌ها دسترسی داشت و اطلاعات کارمند kirk را با استفاده از ویژگی‌ها و متد کلاس Person ذخیره کرد. با استفاده از دستور type()  نیز می‌توان کلاس شی تعریف شده را مشخص کرد. در مثال زیر، نحوه کاربرد این تابع ارائه شده است:

1print(type(kirk))

خروجی قطعه کد بالا به صورت   است که این خروجی نشان می‌دهد شی kirk به عنوان نمونه‌ای از کلاس Person محسوب می‌شود. پس از ساخت کلاس Person و تعریف شی از این کلاس، می‌توان با استفاده از تابع isinstance() مشخص کرد که آیا شی، نمونه‌ای از کلاس Person است؟ قطعه کد زیر، نحوه استفاده از این تابع را نشان می‌دهد.

1kirk = Person()
2print(isinstance(kirk, Person))  # True

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

 

تعریف ویژگی های کلاس در پایتون

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

دو نوع ویژگی می‌توانیم در کلاس پایتون بسازیم که در ادامه به آن‌ها اشاره شده است:

  • «متغیرهای کلاس» (Class Variables)
  • «متغیرهای وهله‌ای» (Instance Variables)

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

متغیر کلاس در پایتون چیست ؟

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

در قطعه کد زیر، متغیر‌های extension  و version  مثال‌هایی از متغیرهای کلاس HtmlDocument  هستند.

1class HtmlDocument:
2    extension = 'html'
3    version = '5'

به منظور دسترسی به مقادیر متغیرهای کلاس، می‌توان از نام کلاس HtmlDocument به همراه علامت نقطه ( .  ) و سپس نام متغیر کلاس استفاده کرد.

1class HtmlDocument:
2    extension = 'html'
3    version = '5'
4
5print(HtmlDocument.extension) # html
6print(HtmlDocument.version) # 5

همچنین، می‌توان شیئی از کلاس HtmlDocument ساخت و با استفاده از نام شی به متغیرهای کلاس دسترسی داشت.

1class HtmlDocument: 
2    extension = 'html' 
3    version = '5' 
4
5object = HtmlDocument() 
6print(object.extension) # html 
7print(object.version) # 5

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

1class HtmlDocument: 
2extension = 'html' 
3version = '5' 
4
5print(HtmlDocument.media_type) 

در قطعه کد بالا، متغیر medi_TYPE در کلاس HtmlDocument تعریف نشده است. به همین خاطر، در زمان اجرای قطعه کد بالا، خطای زیر رخ می‌دهد:

AttributeError: type object 'HtmlDocument' has no attribute 'media_type'

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

1class HtmlDocument:
2    extension = 'html'
3    version = '5'
4
5extension = getattr(HtmlDocument, 'extension')
6version = getattr(HtmlDocument, 'version')
7
8print(extension)  # html
9print(version)  # 5

به منظور تغییر مقادیر متغیرهای کلاس می‌توان از نام کلاس و نام شی به روش زیر استفاده کرد:

1HtmlDocument.version = 10
2object.version = 10

به علاوه،‌ تابع setattr() را نیز می‌توان برای تغییر مقادیر متغیرهای کلاس به کار برد. در قطعه کد زیر، نحوه استفاده از این تابع نشان داده شده است.

1setattr(HtmlDocument, 'version', 10)

به منظور اضافه کردن متغیرهای کلاس جدید می‌توان از سه روش استفاده کرد. روش اول، اضافه کردن نام متغیر کلاس به همراه مقدار آن درون کلاس تعریف شده است. در روش دوم، می‌توان از بیرون کلاس، متغیر کلاس جدیدی را به کلاس تعریف شده از قبل، به روش زیر اضافه کرد:

1HtmlDocument.media_type = 'text/html'
2print(HtmlDocument.media_type) # text/html

روش سوم برای اضافه کردن متغیر کلاس جدید به کلاس از پیش تعریف شده، استفاده از تابع setattr() است. در قطعه کد زیر، نحوه استفاده از این تابع نشان داده شده است:

1setattr(HtmlDocument, media_type, 'text/html')
2print(HtmlDocument.media_type) # text/html

شی گرایی در پایتون این امکان را به برنامه‌نویسان می‌دهد تا وقتی به متغیر کلاسی احتیاج نداشتند، آن متغیر را با استفاده از تابع delattr() و کلمه کلیدی del از برنامه حذف کنند. در قطعه کد زیر، نحوه استفاده از این دو روش برای حذف متغیرهای کلاس HtmlDocument ملاحظه می‌شود.

1delattr(HtmlDocument, 'version')
2del HtmlDocument.extension

متغیرهای وهله ای در کلاس های پایتون

متغیرهای نمونه، متغیرهایی هستند که درون توابع (متدها) و «سازنده‌ها» (Constructors) تعریف می‌شوند. مقادیر این متغیرها برای هر یک از اشیای ساخته شده از یک کلاس مشابه، متفاوت هستند.

به عبارتی، این نوع متغیرها مختص اشیاء هستند و مقادیرشان بین اشیاء مختلف کلاس به اشتراک گذاشته نمی‌شوند. به منظور مقداردهی اولیه متغیرهای نمونه، می‌توان از متد __init__() درون کلاس استفاده کرد. زمانی که شی جدیدی را از یک کلاس ایجاد می‌کنید، به‌طور خودکار، متد __init__() فراخوانی می‌شود. بدین ترتیب، می‌توان از این متد برای مقداردهی اولیه مشخصه‌های اشیاء استفاده کرد. در قطعه کد زیر، مثالی از نحوه مقداردهی اولیه متغیرهای نمونه در کلاس Person ملاحظه می‌شود.

1class Person:
2    def __init__(self, name, age):
3        self.name = name
4        self.age = age
5
6object_person = Person('John', 25)
7print(f"I'm {object_person.name}. I'm {object_person.age} years old.")

در مثال بالا، متغیرهای name و Page متغیرهای نمونه از کلاس Person هستند و به هنگام ساخت شی object_person ، تابع __init__() فراخوانی می‌شود و مقادیر John و 25 درون متغیرهای نمونه name و age قرار می‌گیرند. همان‌طور که در این مثال ملاحظه می‌کنید، نخستین پارامتر تابع __init__() ، مقدار self است. در زبان برنامه نویسی پایتون، زمانی که با استفاده از نام شی، تابعی (متدی) را فراخوانی می‌کنیم، به‌طور خودکار، خود شی به عنوان اولین پارامتر، به آن تابع ارسال می‌شود.

بنابراین، دستور Person('John',25) در قطعه کد بالا، معادل دستور Person(object_person, 'John',25) است. به همین دلیل، اولین پارامتر تابع درون کلاس، باید پارامتر self باشد که به شی فعلی کلاس اشاره دارد. می‌توان نام self را تغییر داد، اما به منظور فهم دقیق‌تر کدها و پایبند بودن به قراردادهای زبان پایتون، بهتر است که از همین کلمه کلیدی برای اشاره به شی استفاده شود. خروجی قطعه کد بالا در ادامه ملاحظه می‌شود:

I'm John. I'm 22 years old.

در تصویر زیر نیز مثالی برای متغیرهای نمونه یا همان متغیرهای وهله‌ای در شی گرایی پایتون ارائه شده است.

متغیرهای نمونه در کلاس پایتون

تعریف متدهای کلاس در پایتون

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

انواع متدهای درون کلاس به شرح زیر هستند:

  • «متد وهله‌ای» (Instance Method)
  • «متد کلاس» (Class Method)

در ادامه این مطلب، به توضیح انواع متدهای کلاس پایتون به همراه مثال‌های کاربردی پرداخته شده است.

متد وهله ای در کلاس پایتون چیست ؟

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

اولین آرگومان دریافتی این متدها، عبارت self است که به شی اشاره دارد. در ادامه، قطعه کدی ملاحظه می‌شود که نحوه تعریف متد نمونه کلاس را نشان می‌دهد.

1class Request:
2    def send(self):
3        print('Sent')
4
5http_request = Request()
6print(http_request.send()) # Sent

متد کلاس در پایتون

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

متدهای کلاس را می‌توان به دو روش در پایتون ایجاد کرد. در اولین روش، از «دکوراتوری» (Decorator) با نام @classmethod استفاده می‌شود که در ادامه قطعه کدی از کاربرد این دکوراتور در ساخت متد کلاس ارائه شده است.

1from datetime import date
2
3class Student:
4    def __init__(self, name, age):
5        self.name = name
6        self.age = age
7
8    @classmethod
9    def calculate_age(cls, name, birth_year):
10        # calculate age an set it as a age
11        # return new object
12        return cls(name, date.today().year - birth_year)
13
14    def show(self):
15        print(self.name + "'s age is: " + str(self.age))
16
17
18# create new object using the factory method
19joy = Student.calculate_age("Joy", 1995)
20joy.show()

در مثال بالا، متد calculate_age از نوع متد کلاس است. خاطر نشان می‌شود نخستین پارامتر متد کلاس باید مقدار cls باشد که به نام کلاس جاری اشاره می‌کند.

متد کلاس در پایتون

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

1class School:
2    # class variable
3    name = 'ABC School'
4
5    def school_name(cls):
6        print('School Name is :', cls.name)
7
8# create class method
9School.school_name = classmethod(School.school_name)
10
11# call class method
12School.school_name()

اصول شی گرایی در پایتون

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

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

  • اصل «کپسوله‌سازی» (Encapsulation)
  • اصل «انتزاع | تجرید» (Abstraction)
  • «وراثت | ارث‌بری» (Inheritance)
  • «پلی مورفیسم | چندریختی» (Polymorphism)

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

اصل وراثت شی گرایی در پایتون چیست ؟

یکی از اصول شی گرایی در پایتون، اصل وراثت یا ارث‌بری است که در طی این فرآیند، می‌توان کلاسی را ایجاد کرد که ویژگی‌ها و متدهای خود را از کلاس دیگری به ارث ببرد که از قبل تعریف شده است. در این حال، کلاس جدید، کلاس فرزند (Child Class) نام دارد و به کلاسی که مشخصه‌های خود را به کلاس فرزند می‌دهد، کلاس والد (Parent Class) گفته می‌شود.

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

ممکن است فرزند رنگ موی خود را از مادرش به ارث ببرد. با این حال، فرزند تصمیم می‌گیرد رنگ موی خود را تغییر دهد و موی خود را به رنگ صورتی تغییر دهد. با اینکه فرزند، داشتن ویژگی رنگ مو را از پدر و مادر خود به ارث برده است، اما می‌تواند به تصمیم خود، رنگ آن را تغییر دهد و موی خود را به رنگی درآورد که مشابه رنگ موی پدر و مادر خود نیست.

اصل وراثت در شی گرایی در پایتون چیست

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

فرض کنید کلاسی با نام Person ساخته شده است که مشخصه‌های __init__ و greet را دارد. در قطعه کد زیر، مثالی از این کلاس ملاحظه می‌شود.

1class Person:
2    def __init__(self, name):
3        self.name = name
4
5    def greet(self):
6        return f"Hi, it's {self.name}"

حال می‌خواهیم کلاسی با نام Employee ایجاد کنیم که دقیقاً مشخصه‌های کلاس Person را داشته باشد. در قطعه کد زیر، نحوه ساخت کلاس Person ملاحظه می‌شود.

1class Employee:
2    def __init__(self, name, job_title):
3        self.name = name
4        self.job_title = job_title
5
6    def greet(self):
7        return f"Hi, it's {self.name}"

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

1class Employee(Person):
2    def __init__(self, name, job_title):
3        self.name = name
4        self.job_title = job_title

همان‌طور که در قطعه کد بالا دیده می‌شود، می‌توان در خط اعلان کلاس فرزند، نام کلاس والد را در داخل پرانتز قرار داد. بدین ترتیب، کلاس Employee علاوه بر ویژگی‌های name و job_title ، دارای متد greet نیز هست که آن را از کلاس والد خود، یعنی کلاس Person ، به ارث می‌برد. در ادامه، مثالی از نحوه دسترسی به متد greet از طریق شی ساخته شده از کلاس Employee ملاحظه می‌شود.

1employee = Employee('John', 'Python Developer')
2print(employee.greet())

خروجی قطعه کد بالا در ادامه نشان داده شده است:

Hi, it's John

با استفاده از تابع issubclass() می‌توان مشخص کرد که آیا دو کلاس رابطه والد - فرزندی دارند؟ در ادامه، نحوه استفاده از این تابع در زبان پایتون در قالب مثال، نشان داده شده است:

1print(issubclass(Employee, Person)) # True

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

1class Employee:
2    def __init__(self, name, base_pay, bonus):
3        self.name = name
4        self.base_pay = base_pay
5        self.bonus = bonus
6
7    def get_pay(self):
8        return self.base_pay + self.bonus
9
10
11class SalesEmployee(Employee):
12    def __init__(self, name, base_pay, bonus, sales_incentive):
13        super().__init__(name, base_pay, bonus)
14        self.sales_incentive = sales_incentive
15
16    def get_pay(self):
17        return super().get_pay() + self.sales_incentive
18
19
20if __name__ == '__main__':
21    sales_employee = SalesEmployee('John', 5000, 1000, 2000)
22    print(sales_employee.get_pay())  # 8000

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

  • «وراثت منفرد» (Single Inheritance)
  • «وراثت چندگانه» (Multiple Inheritance)
  • «وراثت چند سطحی» (Multi-Level Inheritance)
  • «وراثت سلسله‌مراتبی» (Hierarchical Inheritance)
  • «وراثت ترکیبی» (Hybrid Inheritance)
انواع ارث بری در شی گرایی در پایتون چیست
انواع ارث بری در شی گرایی

در ادامه مطلب حاضر، به توضیح هر یک از انواع وراثت پرداخته می‌شود و مثالی کاربردی از زبان پایتون برای هر یک از آن‌ها ارائه خواهد شد.

وراثت منفرد در پایتون

یکی از انواع نحوه ارث‌بری در شی گرایی، نوع ارث‌بری منفرد است. در این نوع وراثت، هر کلاس فرزند تنها دارای یک کلاس والد است و تمامی ویژگی‌ها و متدهای کلاس والد را به ارث می‌برد. در مثال زیر، نحوه ارث‌بری کلاس فرزند از یک کلاس والد در پایتون ملاحظه می‌شود.

1#syntax_of_single_inheritance
2
3class class1:               #parent_class
4    pass
5
6class class2(class1):       #child_class
7    pass
8
9obj_name = class2()
نوع ارث‌بری مثالی که در بخش پیشین درباره دو کلاس Employee و Person ارائه شد، از نوع وراثت منفرد است.

وراثت چندگانه شی گرایی در پایتون چیست ؟

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

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

1#syntax_of_multiple_inheritance
2
3class parent_1:
4    pass
5
6class parent_2:
7    pass
8
9class child(parent_1,parent_2):
10    pass
11
12obj = child()

مثال زیر نیز، قطعه کدی از زبان پایتون را نشان می‌دهد که در آن دو کلاس والد با نام‌های Brands و Products ساخته شده‌اند و کلاس فرزندی نیز با نام Popularity تعریف شده است که از دو کلاس والد ارث‌بری می‌کند.

1#example_of_multiple_inheritance
2
3class Brands:               #parent_class
4    brand_name_1 = "Amazon"
5    brand_name_2 = "Ebay"
6    brand_name_3 = "OLX"
7    
8class Products:            #child_class
9    prod_1 = "Online Ecommerce Store"
10    prod_2 = "Online Store"
11    prod_3 = "Online Buy Sell Store"
12
13class Popularity(Brands,Products):
14    prod_1_popularity = 100
15    prod_2_popularity = 70
16    prod_3_popularity = 60
17    
18obj_1 = Popularity()          #Object_creation
19print(obj_1.brand_name_1+" is an "+obj_1.prod_1))
20print(obj_1.brand_name_2+" is an "+obj_1.prod_2))
21print(obj_1.brand_name_3+" is an "+obj_1.prod_3))

خروجی قطعه کد بالا، در ادامه ملاحظه می‌شود.

#Output
#Amazon is an Online Ecommerce Store popularity of 100
#Ebay is an Online Store popularity of 70
#OLX is an Online Buy Sell Store popularity of 60

وراثت چندسطحی در شی گرایی در پایتون چیست ؟

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

1#Syntax_of_multilevel_inheritance
2
3class A:
4    pass
5
6class B(A):
7    pass
8
9class C(B):
10    pass
11
12obj = C()

مثال زیر، نمونه‌ای از نحوه پیاده‌سازی اصل وراثت از نوع چندسطحی را در پایتون نشان می‌دهد. در این مثال، کلاس Produsts به عنوان کلاس والد برای کلاس Popularity تعریف شده است که خودش به عنوان کلاس فرزند برای کلاس Brands محسوب می‌شود. بدین ترتیب، کلاس Popularity از مشخصه‌های کلاس‌های Products و Brands ارث‌بری می‌کند.

1class Brands:                      #parent_class
2    brand_name_1 = "Amazon"
3    brand_name_2 = "Ebay"
4    brand_name_3 = "OLX"
5    
6class Products(Brands):            #child_class
7    prod_1 = "Online Ecommerce Store"
8    prod_2 = "Online Store"
9    prod_3 = "Online Buy Sell Store"
10
11class Popularity(Products):        #grand_child_class
12    prod_1_popularity = 100
13    prod_2_popularity = 70
14    prod_3_popularity = 60
15    
16    
17obj_1 = Popularity()          #Object_creation
18print(obj_1.brand_name_1+" is an "+obj_1.prod_1+" popularity of "+str(obj_1.prod_1_popularity))
19print(obj_1.brand_name_2+" is an "+obj_1.prod_2+" popularity of "+str(obj_1.prod_2_popularity))
20print(obj_1.brand_name_3+" is an "+obj_1.prod_3+" popularity of "+str(obj_1.prod_3_popularity))
21
22#Output
23#Amazon is an Online Ecommerce Store popularity of 100
24#Ebay is an Online Store popularity of 70
25#OLX is an Online Buy Sell Store popularity of 60

وراثت سلسله مراتبی شی گرایی در پایتون چیست ؟

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

1#syntax_of_hierarchical_inheritance
2
3class A:                  #parent_class
4    pass
5    
6class B(A):               #child_class
7    pass
8
9class C(A):               #child_class
10    pass
11
12class D(A):               #child_class
13    pass
14
15obj_1 = B()       #Object_creation
16obj_2 = C()
17obj_3 = D()

در مثال زیر، نمونه‌ای از وراثت سلسله مراتبی در پایتون ملاحظه می‌شود. در این مثال، کلاس‌های فرزند Popularity ، Value و Products از تنها کلاس والد Brands ارث‌بری می‌کنند.

1#example
2
3class Brands:                      #parent_class
4    brand_name_1 = "Amazon"
5    brand_name_2 = "Ebay"
6    brand_name_3 = "OLX"
7    
8class Products(Brands):            #child_class
9    prod_1 = "Online Ecommerce Store"
10    prod_2 = "Online Store"
11    prod_3 = "Online Buy Sell Store"
12
13class Popularity(Brands):        #grand_child_class
14    prod_1_popularity = 100
15    prod_2_popularity = 70
16    prod_3_popularity = 60
17
18class Value(Brands):
19    prod_1_value = "Excellent Value"
20    prod_2_value = "Better Value"
21    prod_3_value = "Good Value"
22    
23obj_1 = Products()          #Object_creation
24obj_2 = Popularity()
25obj_3 = Value()
26print(obj_1.brand_name_1+" is an "+obj_1.prod_1)
27print(obj_1.brand_name_1+" is an "+obj_1.prod_1)
28print(obj_1.brand_name_1+" is an "+obj_1.prod_1)

خروجی قطعه کد بالا در ادامه نشان داده شده است:

#Output
#Amazon is an Online Ecommerce Store
#Ebay is an Online Store
#OLX is an Online Buy Sell Store

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

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

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

1#Syntax_Hybrid_inheritance
2
3class PC: 
4	pass
5	
6class Laptop(PC): 
7	pass
8	
9class Mouse(Laptop): 
10	pass
11	
12class Student3(Mouse, Laptop): 
13	pass
14	
15# Driver's code 
16obj = Student3()

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

1#Example_Hybrid_inheritance
2class PC:
3def fun1(self):
4print(“This is PC class)
5
6class Laptop(PC):
7def fun2(self):
8print(“This is Laptop class inheriting PC class)
9
10class Mouse(Laptop):
11def fun3(self):
12print(“This is Mouse class inheriting Laptop class)
13
14class Student(Mouse, Laptop):
15def fun4(self):
16print(“This is Student class inheriting PC and Laptop”)
17
18# Driver’s code
19obj = Student()
20obj1 = Mouse()
21obj.fun4()
22obj.fun3()

اصل کپسوله سازی شی گرایی در پایتون چیست ؟

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

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

  • مشخصه‌هایی از نوع «عمومی» (Public)
  • مشخصه‌هایی از نوع «خصوصی» (Private)
  • مشخصه‌هایی از نوع «محافظت شده» (Protected)
کپسوله سازی در شی گرایی پایتون چیست

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

مشخصه های عمومی کلاس در پایتون

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

در قطعه کد زیر، مثالی از مشخصه‌های عمومی کلاس در زبان پایتون ارائه شده است. در این مثال، ویژگی‌های name و age و متد print_name به عنوان مشخصه‌های عمومی کلاس Person محسوب می‌شوند که با استفاده از شی object_person می‌توان از خارج کلاس، به آن‌ها دسترسی داشت.

1class Person: 
2    def __init__(self, name, age): 
3        self.name = name 
4        self.age = age 
5    def print_name(self):
6        print('The name is :' , self.name)
7
8object_person = Person('John', 25)
9print(object_person.name)
10object_person.print_name()

مشخصه های محافظت شده کلاس در پایتون

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

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

در قطعه کد زیر، مثالی از نحوه کاربرد ویژگی‌های محافظت شده در پایتون ارائه شده است. در این مثال، متغیر _q از نوع متغیر محافظت شده است که نام آن با علامت ( _ ) آغاز می‌شود.

1class Base1:  
2    def __init__(self):  
3          self._q = "Javatpoint"  
4
5obj_1 = Base1()  
6print(obj_1._q)  

خروجی قطعه کد بالا، در ادامه آمده است.

Javatpoint

به منظور تعریف متدهای محافظت شده درون کلاس نیز می‌توان قبل از نام متد، از علامت _ در پایتون استفاده کرد. در قطعه کد زیر، مثالی از نحوه تعریف متد محافظت شده در زبان پایتون ارائه شده است. همان‌طور که در این مثال ملاحظه می‌شود، متد _warmup از نوع متد محافظت شده به حساب می‌آید.

1class Person:
2    def __init__(self, name):
3        self.name = name
4    def _warmup(self):
5        print("{} is warming up".format(self.name))
6jack = Person ('Jack')
7jack._warmup()

خروجی قطعه کد بالا در ادامه ملاحظه می‌شود.

Jack is warming up

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

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

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

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

1class Base1: 
2    def __init__(self): 
3        self.__q = "Javatpoint" 
4
5obj_1 = Base1() 
6print(obj_1.__q)

خروجی قطعه کد بالا، در ادامه ملاحظه می‌شود.

Javatpoint

برای تعریف متدهای خصوصی درون کلاس در زبان پایتون نیز می‌توان قبل از نام متد، از علامت ( __ ) استفاده کرد. در قطعه کد زیر، مثالی از نحوه تعریف متد خصوصی در این زبان برنامه نویسی ارائه شده است.

1class Person: 
2    def __init__(self, name): 
3        self.name = name 
4    def __warmup(self): 
5        print("{} is warming up".format(self.name))
6jack = Person ('Jack')
7jack.__warmup()

خروجی قطعه کد بالا در ادامه ملاحظه می‌شود.

Jack is warming up

اصل انتزاع شی گرایی در پایتون

اصل انتزاع در شی گرایی این امکان را فراهم می‌آورد تا بتوانیم کلاس انتزاعی بسازیم. کلاس Abstract یا همان انتزاعی نمونه‌ اولیه‌ای از یک کلاس است که با تعریف کلاس‌های فرزند می‌توان از متدهای آن کلاس ارث‌بری کرد. به منظور استفاده از کلاس‌های انتزاعی، نمی‌توان به‌طور مستقیم از آن‌ها شی ساخت.

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

متدهای انتزاعی، متدهایی هستند که صرفاً اعلان شده‌اند اما بدنه آن‌ها تعریف نشده است. کلاس‌های انتزاعی زمانی کاربرد دارند که بخواهیم کلاس‌های مختلفی ایجاد کنیم که دارای متدهایی با نام‌های یکسان باشند،‌ اما با استفاده از روش «بازنویسی متد» (Method Overriding)، دستورات متفاوتی را برای این متدها در کلاس‌های فرزند تعریف کنیم.

انتزاع در پایتون

بر خلاف کلاس‌های عادی در پایتون، برای تعریف کلاس انتزاعی باید از کتابخانه  استفاده کرد. به عبارتی،‌ به‌طور مستقیم نمی‌توان کلاس انتزاعی را در زبان پایتون تعریف کرد و باید از کتابخانه‌ای با نام abc استفاده شود. در این کتابخانه، ماژولی با نام ABC وجود دارد که باید برای تعریف کلاس انتزاعی در پایتون فراخوانی شود. برای ساخت متد انتزاعی در پایتون نیز پیش از خط اعلان متد، از دستور abc.abstractmethod استفاده می‌شود.

در قطعه کد زیر، مثالی از نحوه تعریف کلاس انتزاعی در زبان پایتون ارائه شده است. در این مثال، کلاس AbstractClassName از نوع کلاس انتزاعی است که ماژول ABC در خط اعلان این کلاس صدا زده می‌شود. متد abstract_method_name نیز متد انتزاعی کلاس است که با استفاده از دکوراتور @abstractmethod به عنوان متد انتزاعی در نظر گرفته می‌شود.

1from abc import ABC, abstractmethod
2
3
4class AbstractClassName(ABC):
5    @abstractmethod
6    def abstract_method_name(self):
7        pass

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

 

در قطعه کد زیر، مثالی کاربردی از کلاس انتزاعی و نحوه ارث‌بری از آن ارائه شده است. در این مثال، کلاس Employee ، کلاس انتزاعی است که یک متد انتزاعی با نام get_salary دارد. کلاس‌های FulltimeEmployee و HuprlyEmployee کلاس‌های فرزند هستند که با روش بازنویسی متد، بدنه متد get_salary در این کلاس‌ها با دستورات متفاوت تکمیل شده است.

1from abc import ABC, abstractmethod
2
3
4class Employee(ABC):
5    def __init__(self, first_name, last_name):
6        self.first_name = first_name
7        self.last_name = last_name
8
9    @abstractmethod
10    def get_salary(self):
11        pass
12
13class FulltimeEmployee(Employee):
14    def __init__(self, first_name, last_name, salary):
15        super().__init__(first_name, last_name)
16        self.salary = salary
17
18    def get_salary(self):
19        return self.salary
20
21
22class HourlyEmployee(Employee):
23    def __init__(self, first_name, last_name, worked_hours, rate):
24        super().__init__(first_name, last_name)
25        self.worked_hours = worked_hours
26        self.rate = rate
27
28    def get_salary(self):
29        return self.worked_hours * self.rate

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

اصل «چندریختی | پلی مورفیسم» (Polymorphism) یکی دیگر از اصول مهم شی‌گرایی است و به وضعیتی اطلاق می‌شود که ماهیتی نظیر متد، «عملگر» (Operator) یا شی در شرایط مختلف، رفتار متفاوتی داشته باشند.

اصل چندریختی در پایتون با دو مفهوم «بازنویسی متد» (Method Overriding) و «سربارگذاری متد» (Method Overloading) سر و کار دارد. در بخش مربوط به اصل انتزاع به توضیح مفهوم بازنویسی متد پرداخته شد. از این روش در مواقعی استفاده می‌شود که در کلاس فرزند، متدی هم‌نام با متد کلاس والد ایجاد شود، به طوری که این متد شامل دستوراتی مغایر با دستورات متد کلاس والد باشد. به عبارتی، می‌توان گفت با اصل چندریختی، یک متد در کلاس‌های مختلف (در کلاس‌های فرزند) عملکرد متفاوتی دارد.

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

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

1class Example:
2    def multiply(self,a,b,c=1):
3        print(a*b*c)
4
5example = Example()
6example.multiply(5,10)
7example.multiply(2,5,6)

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

 

مدیریت ویژگی در کلاس های پایتون

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

برای اعمال محدودیت بر روی ویژگی‌های کلاس، می‌توان از دو متد getter و setter در داخل کلاس استفاده کرد. به منظور درک بهتر مطلب حاضر، بهتر است مثالی در این باره ارائه کنیم. قطعه کد زیر را در نظر بگیرید که کلاسی را با نام Person با ویژگی‌های name و age تعریف می‌کند.

1class Person:
2    def __init__(self, name, age):
3        self.name = name
4        self.age = age
5
6
7john = Person('John', 18)

از آنجایی که age به عنوان ویژگی نمونه تعریف شده است، می‌توان در ادامه برنامه، مقادیر آن را تغییر داد:

1john.age = 19
2...
3...
4...
5john.age = -1

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

1age = -1
2if age <= 0:
3    raise ValueError('The age must be positive')
4else:
5    john.age = age

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

  • متد getter مقدار ویژگی‌ نمونه را در خروجی بازمی‌گرداند.
  • متد setter مقدار جدیدی را برای ویژگی نمونه لحاظ می‌کند.

در قطعه کد زیر، مثالی از نحوه تعریف متدهای getter و setter برای ویژگی age ارائه شده است. خاطر نشان می‌شود به منظور استفاده از متدهای getter و setter بر اساس قرارداد، نوع ویژگی age را که از نوع عمومی است،‌ به نوع محافظت شده (یعنی _age ) تبدیل می‌کنیم.

1class Person:
2    def __init__(self, name, age):
3        self.name = name
4        self.set_age(age)
5
6    def set_age(self, age):
7        if age <= 0:
8            raise ValueError('The age must be positive')
9        self._age = age
10
11    def get_age(self):
12        return self._age

بر اساس قطعه کد ارائه شده، متد set_age() به عنوان متد setter و متد get_age() به عنوان متد getter محسوب می‌شوند. در متد set_age() مقدار ویژگی age بررسی می‌شود که اگر مقدار آن کم‌تر از عدد صفر باشد، خطایی به کاربر در خروجی نشان بدهد مبنی بر این که مقدار عددی ورودی برای ویژگی سن باید عددی مثبت باشد. متد get_age() نیز مقدار ویژگی age را در خروجی بازمی‌گرداند. در متد __init__() نیز متد set_age() را برای مقداردهی ویژگی age فراخوانی می‌کنیم. بدین ترتیب، چنانچه مقداری منفی را برای ویژگی لحاظ کنیم، با اجرای برنامه با خطای زیر در خروجی مواجه می‌شویم:

ValueError: The age must be positive

 

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

البته شی گرایی در پایتون راه‌حل بهتری را برای رفع مشکل مطرح شده ارائه می‌دهد. چنانچه بخواهید متدهای getter و setter را در کلاس تعریف کنید، بهتر است که از تابع Property  استفاده کنید. این کلاس، دارای چندین آرگومان ورودی است که در ادامه به آن‌ها اشاره شده است:

  • آرگومان fget : نام متد getter را مشخص می‌کند.
  • fset : نام متد setter را مشخص می‌کند.
  • آرگومان fdel : نام متدی است که مشخصه مورد نظر را حذف می‌کند.

در قطعه کد زیر، نحوه استفاده از تابع Property ارائه شده است. بر اساس مثال زیر، در کلاس Person شی جدیدی با نام age از تابع Property ایجاد شده است. شی age به عنوان مشخصه کلاس Person محسوب می‌شود.

1class Person:
2    def __init__(self, name, age):
3        self.name = name
4        self.age = age
5
6    def set_age(self, age):
7        if age <= 0:
8            raise ValueError('The age must be positive')
9        self._age = age
10
11    def get_age(self):
12        return self._age
13
14    age = property(fget=get_age, fset=set_age)
15
16john = Person ('John', 18)
17john.age = 19

چنانچه مقدار ویژگی age را با استفاده از شی john از خارج کلاس تغییر دهید، پایتون متد set_age() را از طریق آرگومان fset فراخوانی می‌کند و مقدار ویژگی age را تغییر می‌دهد. به منظور نمایش مقدار ویژگی age در خروجی با استفاده از شیٔ در خارج از کلاس، می‌توان از دستور زیر استفاده کرد:

1print(john.age)

علاوه بر روش بالا، می‌توان مستقیماً متد get_age() را با استفاده از شی به صورت زیر فراخوانی کرد:

1print(john.get_age())

تمرین برنامه نویسی شی گرا با زبان پایتون

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

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

تمرین ۱. تعریف کلاس در پایتون

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

پاسخ تمرین ۱

پاسخ این تمرین به صورت زیر است:

1class Vehicle:
2    pass

تمرین ۲. تعریف مشخصه های کلاس و شی در پایتون

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

پاسخ تمرین ۲

جواب تمرین ۲ در ادامه آمده است.

1class Vehicle:
2    def __init__(self, max_speed, mileage):
3        self.max_speed = max_speed
4        self.mileage = mileage
5
6modelX = Vehicle(240, 18)
7print(modelX.max_speed, modelX.mileage)

تمرین ۳. ارث بری در پایتون

با زبان پایتون قطعه کدی بنویسید که کلاس فرزندی را با نام Bus ایجاد کند که تمامی مشخصه‌های خود را از کلاس والد Vehicle به ارث می‌برد. مشخصه‌های کلاس والد می‌توانند شامل متغیرهای نمونه name ، max_speed و mileage باشند. سپس مشخصه‌های کلاس فرزند را با مقادیر دلخواه مقداردهی کنید و در نهایت مقادیر آن‌ها را در خروجی نشان دهید.

پاسخ تمرین ۳

پاسخ این تمرین در ادامه مشاهده می‌شود.

1class Vehicle:
2
3    def __init__(self, name, max_speed, mileage):
4        self.name = name
5        self.max_speed = max_speed
6        self.mileage = mileage
7
8class Bus(Vehicle):
9    pass
10
11School_bus = Bus("School Volvo", 180, 12)
12print("Vehicle Name:", School_bus.name, "Speed:", School_bus.max_speed, "Mileage:", School_bus.mileage)

تمرین ۴. باز نویسی متد در کلاس پایتون

با زبان پایتون برنامه‌ای بنویسید که کلاس فرزندی را با نام Bus ایجاد کند که تمامی مشخصه‌های خود را از کلاس والد Vehicle به ارث می‌برد.

مشخصه‌های کلاس والد می‌توانند شامل متغیرهای نمونه name ، max_speed و mileage باشند. همچنین، در کلاس والد، متدی با نام seating_capacity را تعریف کنید که پارامتری با نام seating_capacity دریافت کند. متد seating_capacity را در کلاس فرزند با استفاده از بازنویسی متد به نحوی تغییر دهید که مقدار پیش‌فرض ۵۰ برای پارامتر این تابع در نظر گرفته شود.

پاسخ تمرین ۴

پاسخ تمرین ۴ به صورت زیر است.

1class Vehicle:
2    def __init__(self, name, max_speed, mileage):
3        self.name = name
4        self.max_speed = max_speed
5        self.mileage = mileage
6
7    def seating_capacity(self, capacity):
8        return f"The seating capacity of a {self.name} is {capacity} passengers"
9
10class Bus(Vehicle):
11    # assign default value to capacity
12    def seating_capacity(self, capacity=50):
13        return super().seating_capacity(capacity=50)
14
15School_bus = Bus("School Volvo", 180, 12)
16print(School_bus.seating_capacity())

تمرین ۵. متغیرهای کلاس در پایتون

با استفاده از زبان برنامه نویسی پایتون قطعه کدی برای ساخت کلاس والد با نام Vehicle بنویسید که علاوه بر متغیرهای نمونه name ، max_speed و mileage ، متغیر کلاسی با نام Color نیز داشته باشد تا مقدار این متغیر برای تمامی اشیای ساخته شده از کلاس والد و تمامی کلاس‌های فرزند آن به اشتراک گذاشته شود.

کلاس‌های فرزندی با نام‌های Bus و Car نیز ایجاد کنید که از کلاس Vehicle ارث‌بری می‌کنند. برای هر یک از این کلاس‌های فرزند، شیئی بسازید و مقادیری برای متغیرهای نمونه آن‌ها تعریف کنید. در نهایت، مقادیر تمامی مشخصه‌های اشیاء را در خروجی نشان دهید.

پاسخ تمرین ۵

پاسخ این تمرین در ادامه آمده است.

1class Vehicle:
2    # Class attribute
3    color = "White"
4
5    def __init__(self, name, max_speed, mileage):
6        self.name = name
7        self.max_speed = max_speed
8        self.mileage = mileage
9
10class Bus(Vehicle):
11    pass
12
13class Car(Vehicle):
14    pass
15
16School_bus = Bus("School Volvo", 180, 12)
17print(School_bus.color, School_bus.name, "Speed:", School_bus.max_speed, "Mileage:", School_bus.mileage)
18
19car = Car("Audi Q5", 240, 18)
20print(car.color, car.name, "Speed:", car.max_speed, "Mileage:", car.mileage)

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

کلاس والدی با نام Vehicle و کلاس فرزندی با نام Bus ایجاد کنید و درنهایت مشخص کنید آیا کلاس Bus به عنوان کلاس فرزند برای کلاس Vehicle محسوب می‌شود یا خیر؟

پاسخ تمرین ۶

جواب تمرین ۶ در ادامه ارائه شده است.

1class Vehicle:
2    def __init__(self, name, mileage, capacity):
3        self.name = name
4        self.mileage = mileage
5        self.capacity = capacity
6
7class Bus(Vehicle):
8    pass
9
10School_bus = Bus("School Volvo", 12, 50)
11
12# Python's built-in isinstance() function
13print(isinstance(School_bus, Vehicle))

جمع‌بندی

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

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

بر اساس رای ۰ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
Real PythonPython TutorialjavaTpointPYnative
۲ دیدگاه برای «شی گرایی در پایتون چیست؟ – به زبان ساده + مثال و تمرین»

عالی و جامع بود. دستتون درد نکنه.

نظر شما چیست؟

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