Table of Contents
ایدهٔ وراثت: بازاستفاده و گسترش کلاسها
در فصل شیءگرا (OOP) با مفاهیم کلی کلاس و شیء، ویژگیها و متدها آشنا شدهاید. وراثت (Inheritance) ابزاری است که به شما اجازه میدهد:
- کدی را که قبلاً در یک کلاس نوشتهاید دوباره استفاده کنید،
- یک «نسخهٔ تخصصیتر» از آن کلاس بسازید،
- و فقط تفاوتها را پیادهسازی کنید، نه همهچیز را از نو.
به زبان ساده:
- یک کلاس پدر (پایه / والد) داریم، مثلاً
Animal - از روی آن یک کلاس فرزند (مشتق) میسازیم، مثلاً
Dog - کلاس فرزند، ویژگیها و متدهای کلاس پدر را به ارث میبرد
(انگار نسخهٔ پیشرفتهتر یا خاصتر آن است).
در این فصل فقط با وراثت ساده (تکوراثت) آشنا میشوید؛ مفاهیم پیچیدهتر مثل وراثت چندگانه در این دوره پوشش داده نمیشود.
ساختن کلاس فرزند از روی کلاس پدر
سینتکس وراثت در پایتون:
class Parent:
...
class Child(Parent):
...یعنی در پرانتز بعد از نام کلاس فرزند، نام کلاس پدر را مینویسیم.
یک مثال ساده:
class Animal:
def speak(self):
print("این حیوان صدایی درمیآورد.")
class Dog(Animal):
pass # فعلاً چیزی اضافه نمیکنیم
dog = Dog()
dog.speak()در اینجا:
Animalکلاس پدر است.Dogکلاس فرزند است که ازAnimalارثبری میکند.- چون
Dogچیزی تعریف نکرده، اما ازAnimalارث میبرد، متدspeakرا دارد.
خروجی:
این حیوان صدایی درمیآورد.
نکته: کلمهٔ pass یعنی «هیچ کاری نکن» و فقط برای خالی نماندن بدنهٔ کلاس استفاده شده است.
اضافه کردن ویژگی و متد جدید در کلاس فرزند
کلاس فرزند میتواند موارد جدید داشته باشد که در کلاس پدر وجود ندارند.
class Animal:
def speak(self):
print("این حیوان صدایی درمیآورد.")
class Dog(Animal):
def bark(self):
print("هاپ هاپ!")
dog = Dog()
dog.speak() # از کلاس Animal
dog.bark() # مخصوص کلاس Dogدر این مثال:
speakاز پدر به ارث رسیده است.barkمتدی است که فقطDogدارد.
بازنویسی متدها (Override)
گاهی میخواهید کلاس فرزند همان متد کلاس پدر را داشته باشد،
اما رفتارش متفاوت باشد. در این صورت، متد را در کلاس فرزند بازنویسی میکنید.
class Animal:
def speak(self):
print("این حیوان صدایی درمیآورد.")
class Dog(Animal):
def speak(self): # بازنویسی متد پدر
print("سگ میگوید: هاپ هاپ!")
class Cat(Animal):
def speak(self):
print("گربه میگوید: میاو میاو!")
animal = Animal()
dog = Dog()
cat = Cat()
animal.speak()
dog.speak()
cat.speak()خروجی:
این حیوان صدایی درمیآورد.
سگ میگوید: هاپ هاپ!
گربه میگوید: میاو میاو!
نکتهٔ مهم:
نام متد و تعداد/نام پارامترهای آن در کلاس فرزند باید با کلاس پدر یکی باشد تا واقعاً «بازنویسی» محسوب شود.
وراثت و `__init__` (سازنده)
معمولاً کلاسها یک متد __init__ دارند که هنگام ساخت شیء اجرا میشود.
در وراثت، دو حالت مهم داریم:
- کلاس فرزند از
__init__پدر همانطور که هست استفاده کند؛ - کلاس فرزند
__init__خودش را تعریف کند، ولی همزمان کارهای__init__پدر را هم انجام دهد.
استفادهٔ مستقیم از `__init__` پدر
اگر در کلاس فرزند __init__ تعریف نکنید، به طور خودکار از __init__ پدر استفاده میشود.
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
pass
dog = Dog("آلفا")
print(dog.name)اینجا:
Dogهیچ__init__ای ندارد.Dog("آلفا")در واقع ازAnimal.__init__استفاده میکند.
استفاده از `super()` در کلاس فرزند
اگر بخواهید در کلاس فرزند:
- هم کارهای
__init__پدر انجام شود، - هم چیزهای جدید اضافه کنید،
از تابع super() استفاده میکنید.
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
# صدا زدن سازندهٔ کلاس پدر
super().__init__(name)
# ویژگی اضافه مخصوص Dog
self.breed = breed
dog = Dog("آلفا", "ژرمن شپرد")
print(dog.name)
print(dog.breed)اینجا:
super().__init__(name)مطمئن میکندnameدر کلاس پدر تنظیم شود.breedویژگی جدید مخصوصDogاست.
اگر super().__init__ را فراموش کنید، ویژگیهایی که در سازندهٔ کلاس پدر تعریف میشوند، در شیء فرزند وجود نخواهند داشت.
مثال کلی: کلاس پدر «وسیلهٔ نقلیه» و کلاسهای فرزند
یک مثال کمی کاملتر:
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def info(self):
print(f"وسیله نقلیه: {self.brand} {self.model}")
class Car(Vehicle):
def __init__(self, brand, model, doors):
super().__init__(brand, model)
self.doors = doors
def info(self): # بازنویسی متد info
print(f"ماشین: {self.brand} {self.model} با {self.doors} در")
class Motorcycle(Vehicle):
def info(self): # اینجا فقط پیام را تغییر میدهیم
print(f"موتور: {self.brand} {self.model}")
car = Car("Toyota", "Corolla", 4)
moto = Motorcycle("Yamaha", "R3")
car.info()
moto.info()نکات مهم در این مثال:
Vehicleکلاس عمومیتر (پدر) است.CarوMotorcycleکلاسهای خاصتر (فرزند) هستند.- هر دو از ویژگیهای
brandوmodelاستفاده میکنند که درVehicleتعریف شده. Carیک ویژگی اضافهdoorsدارد.- هر دو کلاس فرزند متد
infoرا بازنویسی کردهاند تا پیام مناسب خود را نمایش دهند.
چرا وراثت مفید است؟
چند مزیت مهم وراثت:
- کاهش تکرار کد
چیزهای مشترک را یکبار در کلاس پدر مینویسید. - مدلسازی بهتر دنیای واقعی
مثلاً: Person→Student،TeacherShape→Circle،Rectangle- گسترش آسان کد
بدون دستزدن به کد کلاس پدر، میتوانید کلاسهای فرزند جدید بسازید.
نکات و اشتباهات رایج در وراثت
- فراموش کردن
super().__init__در کلاس فرزند؛ در نتیجه، شیء بعضی ویژگیهای لازم را ندارد. - بازنویسی متد با نام اشتباه (مثلاً
speekبهجایspeak)، که باعث میشود متد پدر همچنان بدون تغییر اجرا شود. - استفاده از وراثت وقتی که واقعاً «رابطهٔ پدر–فرزندی» منطقی وجود ندارد.
(برای این دوره، کافی است فقط در مثالهایی استفاده کنید که «الف یک نوعی از ب است»؛
مثلDogیک نوعی ازAnimalاست.)
تمرینهای پیشنهادی
برای تمرین (نیازی نیست در این فصل حل شوند، فقط پیشنهاد هستند):
- کلاسی به نام
Personبسازید با ویژگیهایnameوageو یک متدintroduceکه خود را معرفی کند.
سپس کلاسStudentرا بسازید که ازPersonارث ببرد و ویژگیstudent_idداشته باشد و متدintroduceرا طوری بازنویسی کند که شمارهٔ دانشجویی را هم بگوید. - کلاسی به نام
Shapeبسازید با متدarea(فعلاً فقطprintکند «مساحت تعریف نشده است»).
سپس کلاسهایRectangleوCircleرا از آن ارث ببرید و متدareaرا طوری بازنویسی کنید که مساحت را حساب و چاپ کنند.
در فصلهای بعدی مثالهای بیشتری از ترکیب وراثت با دیگر مفاهیم OOP خواهید دید.