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


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

مفاهیم اولیه شی‌گرایی

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

1. کلاس‌ها (Classes)

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

کلاس‌ها مانند قالب‌هایی هستند که مشخص می‌کنند هر شیء از آن‌ها چه ویژگی‌ها و رفتارهایی خواهد داشت.

مثال:

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
    
    def bark(self):
        print(f"{self.name} says Woof!")

در اینجا، Dog یک کلاس است که ویژگی‌های name و breed را به اشیاء Dog اختصاص می‌دهد و همچنین یک متد bark دارد که صدای پارس سگ را شبیه‌سازی می‌کند.

 

2. اشیاء (Objects)

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

مثال:

dog1 = Dog("Buddy", "Golden Retriever")
dog2 = Dog("Max", "Bulldog")

dog1.bark()  # خروجی: Buddy says Woof!
dog2.bark()  # خروجی: Max says Woof!

در اینجا، dog1 و dog2 اشیاء از کلاس Dog هستند و هرکدام ویژگی‌های خاص خود را دارند.

 

3. ویژگی‌ها (Attributes)

ویژگی‌ها همان داده‌هایی هستند که به یک شیء اختصاص می‌یابند. این داده‌ها می‌توانند شامل هر نوع اطلاعاتی باشند که به شیء مربوط می‌شود. ویژگی‌ها معمولاً در متد __init__ تعریف می‌شوند.

مثال:

class Car:
    def __init__(self, brand, model, year):
        self.brand = brand
        self.model = model
        self.year = year

my_car = Car("Toyota", "Corolla", 2020)
print(my_car.brand)  # خروجی: Toyota
print(my_car.model)  # خروجی: Corolla
print(my_car.year)   # خروجی: 2020

در اینجا، ویژگی‌های brand, model, و year به شیء my_car اختصاص داده شده‌اند.

 

4. متدها (Methods)

متدها رفتارهایی هستند که کلاس‌ها می‌توانند داشته باشند. این متدها عملکردهایی را روی ویژگی‌های شیء انجام می‌دهند یا عملیاتی را اجرا می‌کنند. متدها به‌طور معمول در داخل کلاس تعریف می‌شوند و به کمک `self` به ویژگی‌های شیء دسترسی دارند.

مثال:

class Circle:
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2
    
    def circumference(self):
        return 2 * 3.14 * self.radius

circle = Circle(5)
print(circle.area())           # خروجی: 78.5
print(circle.circumference())  # خروجی: 31.4

در اینجا، متدهای area و circumference برای محاسبه مساحت و محیط دایره تعریف شده‌اند.

 

5. ارث‌بری (Inheritance)

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

مثال:

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        raise NotImplementedError("Subclass must implement abstract method")

class Dog(Animal):
    def speak(self):
        return f"{self.name} barks!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} meows!"

dog = Dog("Buddy")
cat = Cat("Whiskers")

print(dog.speak())  # خروجی: Buddy barks!
print(cat.speak())  # خروجی: Whiskers meows!

در اینجا، کلاس Dog و Cat از کلاس پایه Animal ارث می‌برند و متد speak را پیاده‌سازی می‌کنند.

 

6. پلی‌مورفیسم (Polymorphism)

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

مثال:

class Bird:
    def speak(self):
        return "Chirp chirp!"

class Dog:
    def speak(self):
        return "Woof woof!"

def animal_sound(animal):
    print(animal.speak())

bird = Bird()
dog = Dog()

animal_sound(bird)  # خروجی: Chirp chirp!
animal_sound(dog)   # خروجی: Woof woof!

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

 

7. انسداد داده‌ها (Encapsulation)

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

مثال:

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance  # ویژگی خصوصی
    
    def deposit(self, amount):
        self.__balance += amount
    
    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Insufficient balance.")
    
    def get_balance(self):
        return self.__balance

account = BankAccount("Alice", 1000)
account.deposit(500)
account.withdraw(200)
print(account.get_balance())  # خروجی: 1300

در اینجا، ویژگی __balance خصوصی است و نمی‌توان به‌طور مستقیم به آن دسترسی داشت. فقط از طریق متدهای deposit, withdraw, و get_balance می‌توان به آن دسترسی پیدا کرد.

 

تعریف و استفاده از متدها و ویژگی‌ها

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

1. ویژگی‌ها (Attributes)

ویژگی‌ها (که به آن‌ها فیلدها یا خواص نیز گفته می‌شود) داده‌هایی هستند که به هر شیء اختصاص داده می‌شوند. ویژگی‌ها معمولاً در متد __init__ تعریف می‌شوند و به‌وسیله self به شیء اختصاص داده می‌شوند. ویژگی‌ها می‌توانند از هر نوع داده‌ای مانند اعداد، رشته‌ها، لیست‌ها و غیره باشند.

ویژگی‌های عمومی

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

مثال:

class Car:
    def __init__(self, brand, model, year):
        self.brand = brand  # ویژگی عمومی
        self.model = model  # ویژگی عمومی
        self.year = year    # ویژگی عمومی

car1 = Car("Toyota", "Corolla", 2020)
print(car1.brand)  # خروجی: Toyota
print(car1.year)   # خروجی: 2020

در اینجا، ویژگی‌های brand, model, و year به‌طور عمومی در دسترس هستند و می‌توانیم به‌راحتی به آن‌ها دسترسی داشته باشیم.

 

ویژگی‌های خصوصی

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

مثال:

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance  # ویژگی خصوصی
    
    def deposit(self, amount):
        self.__balance += amount
    
    def get_balance(self):
        return self.__balance

account = BankAccount("Alice", 1000)
print(account.get_balance())  # خروجی: 1000
# دسترسی به ویژگی خصوصی خارج از کلاس با خطا مواجه می‌شود
# print(account.__balance)  # خطا: AttributeError

در اینجا، ویژگی __balance خصوصی است و نمی‌توان به آن مستقیماً دسترسی پیدا کرد. برای دسترسی به آن باید از متدهای عمومی مانند get_balance استفاده کرد.

 

2. متدها (Methods)

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

متدهای معمولی

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

مثال:

class Rectangle:
    def __init__(self, length, width):
        self.length = length
        self.width = width
    
    def area(self):
        return self.length * self.width
    
    def perimeter(self):
        return 2 * (self.length + self.width)

rect = Rectangle(5, 3)
print(rect.area())       # خروجی: 15
print(rect.perimeter())  # خروجی: 16

در اینجا، متدهای area و perimeter برای محاسبه مساحت و محیط مستطیل ایجاد شده‌اند. این متدها از ویژگی‌های length و width استفاده می‌کنند.

 

متدهای سازنده (__init__)

متد __init__ متدی است که برای ایجاد و مقداردهی اولیه به ویژگی‌های یک شیء به‌طور خودکار فراخوانی می‌شود. این متد به‌عنوان یک سازنده عمل می‌کند و معمولاً برای تعیین مقادیر اولیه ویژگی‌ها در نظر گرفته می‌شود.

مثال:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person("John", 30)
print(person.name)  # خروجی: John
print(person.age)   # خروجی: 30

در اینجا، متد __init__ برای مقداردهی اولیه به ویژگی‌های name و age استفاده شده است.

 

متدهای ویژه (Special Methods)

در پایتون، متدهای خاص یا ویژه‌ای وجود دارند که برای انجام عملیات خاص روی اشیاء استفاده می‌شوند. این متدها معمولاً با دو خط تیره (__) شروع و تمام می‌شوند. یکی از مشهورترین این متدها، متد __str__ است که برای نمایش رشته‌ای از شیء به کار می‌رود.

مثال:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        return f"Person({self.name}, {self.age})"

person = Person("Alice", 28)
print(person)  # خروجی: Person(Alice, 28)

در اینجا، متد __str__ برای ارائه نمایی متنی از شیء Person استفاده شده است.

 

3. استفاده از ویژگی‌ها و متدها در ترکیب با یکدیگر

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

مثال:

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance  # ویژگی خصوصی
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
        else:
            print("Amount should be positive.")
    
    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Insufficient balance.")
    
    def get_balance(self):
        return self.__balance

account = BankAccount("John", 500)
account.deposit(200)
account.withdraw(100)
print(account.get_balance())  # خروجی: 600

در اینجا، ویژگی __balance توسط متدهای deposit و withdraw تغییر می‌کند و از متد get_balance برای دریافت موجودی استفاده می‌شود.

 

آشنایی با متدهای ویژه (مثل __init__ و __str__)

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

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

 

1. متد __init__: سازنده کلاس

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

مثال:

class Car:
    def __init__(self, brand, model, year):
        self.brand = brand
        self.model = model
        self.year = year

car1 = Car("Toyota", "Corolla", 2020)
print(car1.brand)  # خروجی: Toyota
print(car1.model)  # خروجی: Corolla
print(car1.year)   # خروجی: 2020

در اینجا، متد __init__ برای مقداردهی اولیه به ویژگی‌های brand, model, و year استفاده شده است.

 

2. متد __str__: نمایش متنی شیء

متد __str__ به شما این امکان را می‌دهد که نحوه نمایش یک شیء در قالب رشته را سفارشی‌سازی کنید. زمانی که یک شیء را چاپ می‌کنید (برای مثال با استفاده از دستور print())، پایتون به‌طور پیش‌فرض از متد __str__ برای تبدیل شیء به رشته استفاده می‌کند.

مثال:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        return f"Person({self.name}, {self.age})"

person = Person("Alice", 28)
print(person)  # خروجی: Person(Alice, 28)

در اینجا، متد __str__ نحوه نمایش شیء Person را تعیین کرده است. به‌طور پیش‌فرض، پایتون به جای نمایش محتوای دقیق شیء، آدرس حافظه آن را چاپ می‌کند، اما با استفاده از __str__ می‌توانیم نمایش را سفارشی‌سازی کنیم.

 

3. متد __repr__: نمایش رسمی شیء

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

مثال:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

person = Person("Bob", 35)
print(repr(person))  # خروجی: Person('Bob', 35)

در اینجا، متد __repr__ خروجی‌ای ارائه می‌دهد که می‌تواند برای ایجاد یک شیء جدید مشابه به شیء موجود استفاده شود. برای مثال، خروجی Person('Bob', 35) به راحتی می‌تواند برای ساخت شیء جدیدی مشابه استفاده شود.

 

4. متد __add__: عملگر جمع

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

مثال:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)

point1 = Point(2, 3)
point2 = Point(4, 5)
result = point1 + point2
print(result.x, result.y)  # خروجی: 6 8

در اینجا، متد __add__ به ما این امکان را می‌دهد که دو شیء از نوع Point را با هم جمع کنیم. نتیجه جمع دو نقطه جدیدی به‌عنوان شیء باز می‌گرداند.

 

5. متد __len__: طول شیء

متد __len__ به شما این امکان را می‌دهد که طول یک شیء را تعیین کنید. این متد معمولاً برای کلاس‌هایی استفاده می‌شود که شامل مجموعه‌هایی مانند لیست‌ها یا رشته‌ها هستند.

مثال:

class MyList:
    def __init__(self, elements):
        self.elements = elements
    
    def __len__(self):
        return len(self.elements)

my_list = MyList([1, 2, 3, 4, 5])
print(len(my_list))  # خروجی: 5

در اینجا، متد __len__ تعداد عناصر موجود در شیء MyList را برمی‌گرداند.

 

6. متد __eq__: مقایسه اشیاء

متد __eq__ برای مقایسه دو شیء از نوع کلاس شما استفاده می‌شود. این متد زمانی فراخوانی می‌شود که عملگر مقایسه == را روی اشیاء اعمال می‌کنید.

مثال:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __eq__(self, other):
        return self.name == other.name and self.age == other.age

person1 = Person("John", 30)
person2 = Person("John", 30)
person3 = Person("Alice", 28)

print(person1 == person2)  # خروجی: True
print(person1 == person3)  # خروجی: False

در اینجا، متد __eq__ به‌طور سفارشی مقایسه دو شیء از نوع Person را انجام می‌دهد.

 

استفاده از ارث‌بری برای ساخت کلاس‌های پیشرفته‌تر

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

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

 

1. ارث‌بری ساده: به ارث بردن ویژگی‌ها و متدها

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

مثال:

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        print(f"{self.name} makes a sound")

class Dog(Animal):
    def __init__(self, name, breed):
        # فراخوانی سازنده کلاس والد
        super().__init__(name)
        self.breed = breed
    
    def speak(self):
        print(f"{self.name} barks")

class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name)
        self.color = color
    
    def speak(self):
        print(f"{self.name} meows")

# استفاده از کلاس‌ها
dog = Dog("Buddy", "Golden Retriever")
dog.speak()  # خروجی: Buddy barks

cat = Cat("Whiskers", "Black")
cat.speak()  # خروجی: Whiskers meows

در این مثال، Dog و Cat از کلاس Animal ارث‌بری کرده‌اند. هر کدام از این کلاس‌ها متد speak() را به‌طور خاص برای خود بازنویسی کرده‌اند (Overriding) تا صدای مخصوص به خود را داشته باشند. همچنین، از متد super() برای فراخوانی سازنده کلاس والد استفاده شده است.

 

2. ارث‌بری و بازنویسی متدها (Method Overriding)

گاهی اوقات، شما نیاز دارید تا متدهای یک کلاس را در کلاس فرزند بازنویسی کنید تا رفتار متفاوتی از کلاس والد داشته باشید. این فرآیند به نام **بازنویسی متد** (Method Overriding) شناخته می‌شود.

مثال:

class Shape:
    def __init__(self, name):
        self.name = name
    
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        super().__init__("Circle")
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius * self.radius

class Square(Shape):
    def __init__(self, side):
        super().__init__("Square")
        self.side = side
    
    def area(self):
        return self.side * self.side

circle = Circle(5)
square = Square(4)

print(f"{circle.name} area: {circle.area()}")  # خروجی: Circle area: 78.5
print(f"{square.name} area: {square.area()}")  # خروجی: Square area: 16

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

 

3. ارث‌بری چندگانه (Multiple Inheritance)

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

مثال:

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        print(f"{self.name} makes a sound")

class Walker:
    def walk(self):
        print(f"{self.name} is walking")

class Dog(Animal, Walker):
    def __init__(self, name, breed):
        Animal.__init__(self, name)
        self.breed = breed

# استفاده از ارث‌بری چندگانه
dog = Dog("Buddy", "Golden Retriever")
dog.speak()  # خروجی: Buddy makes a sound
dog.walk()   # خروجی: Buddy is walking

در این مثال، کلاس Dog هم از کلاس Animal و هم از کلاس Walker ارث‌بری می‌کند. بنابراین، شیء dog از ویژگی‌ها و متدهای هر دو کلاس استفاده می‌کند.

 

4. ارث‌بری و استفاده از super()

هنگامی که در ارث‌بری چندگانه استفاده می‌کنید، ممکن است بخواهید متدهای کلاس‌های والد را فراخوانی کنید. در این حالت، پایتون از یک روش خاص به نام **روش MRO** (Method Resolution Order) برای تعیین ترتیب فراخوانی متدها استفاده می‌کند. برای این کار می‌توانید از متد `super()` استفاده کنید.

مثال:

class A:
    def __init__(self):
        print("A initialized")
    
    def method(self):
        print("A method")

class B(A):
    def __init__(self):
        super().__init__()
        print("B initialized")
    
    def method(self):
        super().method()
        print("B method")

class C(B):
    def __init__(self):
        super().__init__()
        print("C initialized")
    
    def method(self):
        super().method()
        print("C method")

c = C()
c.method()

خروجی:

A initialized
B initialized
C initialized
A method
B method
C method

در اینجا، از super() برای فراخوانی متدهای کلاس‌های والد به ترتیب استفاده شده است. پایتون از روش MRO برای مشخص کردن ترتیب فراخوانی استفاده می‌کند و این ترتیب از سمت چپ به راست در سلسله‌مراتب ارث‌بری است.

 

کار با پلی‌مورفیسم (چندریختی) در کلاس‌ها

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

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

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

1. پلی‌مورفیسم زمانی (Runtime Polymorphism)

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

مثال:

class Animal:
    def speak(self):
        print("The animal makes a sound")

class Dog(Animal):
    def speak(self):
        print("The dog barks")

class Cat(Animal):
    def speak(self):
        print("The cat meows")

# استفاده از پلی‌مورفیسم
animals = [Dog(), Cat(), Animal()]

for animal in animals:
    animal.speak()

خروجی:

The dog barks
The cat meows
The animal makes a sound

در این مثال، لیستی از اشیاء از کلاس‌های مختلف (Dog، Cat، Animal) داریم. وقتی که متد speak() را فراخوانی می‌کنیم، هر شیء بسته به نوع خود رفتار متفاوتی از خود نشان می‌دهد. این همان پلی‌مورفیسم است: یک نام متد (speak) که در کلاس‌های مختلف رفتارهای متفاوتی دارد.

 

2. پلی‌مورفیسم با استفاده از متدهای بازنویسی‌شده (Overriding)

در پایتون، پلی‌مورفیسم معمولاً با بازنویسی متدها (method overriding) در کلاس‌های فرزند به دست می‌آید. وقتی که یک متد در کلاس فرزند بازنویسی می‌شود، رفتار جدیدی برای آن متد ارائه می‌دهیم.

مثال:

class Shape:
    def area(self):
        raise NotImplementedError("Subclass must implement abstract method")

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius * self.radius

class Square(Shape):
    def __init__(self, side):
        self.side = side
    
    def area(self):
        return self.side * self.side

shapes = [Circle(5), Square(4)]

for shape in shapes:
    print(f"Area: {shape.area()}")

خروجی:

Area: 78.5
Area: 16

در این مثال، متد area() در کلاس‌های Circle و Square بازنویسی شده است. این متد در کلاس پایه Shape به‌صورت abstract (غیر پیاده‌سازی‌شده) تعریف شده و هر کلاس فرزند موظف است آن را پیاده‌سازی کند. وقتی که متد area() را برای هر کدام از اشیاء فراخوانی می‌کنیم، پایتون بر اساس نوع شیء، متد مناسب را فراخوانی می‌کند.

 

3. استفاده از پلی‌مورفیسم با متدهای عمومی

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

مثال:

class Employee:
    def get_salary(self):
        print("Getting salary for a general employee")

class Manager(Employee):
    def get_salary(self):
        print("Getting salary for a manager")

class Developer(Employee):
    def get_salary(self):
        print("Getting salary for a developer")

def display_salary(employee):
    employee.get_salary()

# استفاده از پلی‌مورفیسم
emp = Employee()
mgr = Manager()
dev = Developer()

display_salary(emp)  # خروجی: Getting salary for a general employee
display_salary(mgr)  # خروجی: Getting salary for a manager
display_salary(dev)  # خروجی: Getting salary for a developer

در اینجا، ما یک متد عمومی به نام display_salary داریم که از آن برای هر نوع کارمند استفاده می‌کنیم. به لطف پلی‌مورفیسم، هر نوع کارمند (مثل Manager یا Developer) می‌تواند متد get_salary خود را اجرا کند.

 

4. پلی‌مورفیسم و استفاده از کلاس‌های abstract

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

مثال:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        print("The dog barks")

class Cat(Animal):
    def speak(self):
        print("The cat meows")

animals = [Dog(), Cat()]

for animal in animals:
    animal.speak()

خروجی:

The dog barks
The cat meows

در این مثال، از کلاس ABC و دکوریتور @abstractmethod برای تعریف یک متد abstract به نام speak استفاده کرده‌ایم. این متد در کلاس‌های Dog و Cat پیاده‌سازی می‌شود و به‌این‌ترتیب پلی‌مورفیسم را با استفاده از کلاس‌های abstract پیاده‌سازی کرده‌ایم.

 

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

کپسوله‌سازی و جلوگیری از دسترسی به داده‌های خصوصی

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

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

1. استفاده از ویژگی‌های خصوصی با یک زیرخط (Underscore)

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

- یک زیرخط (_): نشان‌دهنده این است که ویژگی یا متد خصوصی است، اما هنوز قابل دسترسی است.

- دو زیرخط (__): نشان‌دهنده این است که ویژگی یا متد خصوصی‌تر است و دسترسی به آن از خارج از کلاس محدودتر است.

مثال:

class Person:
    def __init__(self, name, age):
        self._name = name  # ویژگی نیمه‌خصوصی
        self.__age = age   # ویژگی کاملاً خصوصی

    def get_name(self):
        return self._name

    def get_age(self):
        return self.__age

# ایجاد شیء از کلاس
person = Person("Ali", 30)

# دسترسی به ویژگی‌های خصوصی
print(person.get_name())  # خروجی: Ali
print(person.get_age())   # خروجی: 30

# دسترسی مستقیم به ویژگی‌های خصوصی از خارج از کلاس
print(person._name)  # خروجی: Ali (این کار مجاز است، اما توصیه نمی‌شود)
# print(person.__age)  # این خط ارور می‌دهد چون __age خصوصی است

در این مثال، ویژگی __age در کلاس Person با دو زیرخط مشخص شده است، بنابراین نمی‌توان از خارج از کلاس به آن دسترسی پیدا کرد. همچنین، ویژگی _name با یک زیرخط مشخص شده که به‌طور غیررسمی نشان‌دهنده خصوصی بودن آن است، اما همچنان می‌توان به آن دسترسی داشت.

 

2. استفاده از متدهای خصوصی و getter/setter

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

Getter: متدی است که داده‌های خصوصی را برمی‌گرداند.

Setter: متدی است که داده‌های خصوصی را تنظیم می‌کند.

مثال:

class Car:
    def __init__(self, model, price):
        self.__model = model  # خصوصی
        self.__price = price  # خصوصی

    # متد getter برای دسترسی به مدل
    def get_model(self):
        return self.__model

    # متد setter برای تغییر قیمت
    def set_price(self, price):
        if price > 0:
            self.__price = price
        else:
            print("Invalid price!")

    # متد getter برای دسترسی به قیمت
    def get_price(self):
        return self.__price

# ایجاد شیء از کلاس
car = Car("Tesla", 50000)

# استفاده از متدهای getter و setter
print(car.get_model())  # خروجی: Tesla
print(car.get_price())  # خروجی: 50000

car.set_price(55000)  # تغییر قیمت به 55000
print(car.get_price())  # خروجی: 55000

car.set_price(-1000)  # تلاش برای تنظیم قیمت نامعتبر

در این مثال، متد get_model() برای دسترسی به ویژگی خصوصی __model و متد get_price() برای دسترسی به ویژگی خصوصی __price استفاده می‌شود. همچنین، از متد set_price() برای تغییر قیمت استفاده می‌کنیم. این متد چک می‌کند که قیمت ورودی معتبر باشد یا خیر، و در صورتی که نامعتبر باشد، از تغییر آن جلوگیری می‌کند.

 

3. کپسوله‌سازی و امنیت داده‌ها

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

مثال:

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
        else:
            print("Amount must be positive.")

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Invalid withdrawal amount.")

    def get_balance(self):
        return self.__balance

# ایجاد شیء از کلاس
account = BankAccount(1000)

# استفاده از متدها برای انجام تراکنش‌ها
account.deposit(500)
print(account.get_balance())  # خروجی: 1500

account.withdraw(200)
print(account.get_balance())  # خروجی: 1300

account.withdraw(2000)  # تلاش برای برداشت بیش از موجودی

در این مثال، کلاس BankAccount دارای ویژگی خصوصی __balance است. با استفاده از متدهای deposit() و withdraw()، می‌توانیم موجودی حساب را به‌طور کنترل‌شده تغییر دهیم و از برداشت بیش از موجودی جلوگیری کنیم.

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

 

تمرین سطح مبتدی

 

3. کلاس حیوانات

یک کلاس با نام حیوانات بسازید که ویژگی‌هایی مانند نام و صدا را داشته باشد. برای هر نوع حیوان، یک شیء ایجاد کنید که صداهای مختلف تولید کند (مثلاً سگ: "ووف"، گربه: "میو").

تمرین:

برای هر حیوان یک شیء بسازید و متد صدا زدن آن را فراخوانی کنید.

 

 

2. کلاس حساب بانکی

یک کلاس با نام حساب بانکی بسازید که ویژگی‌هایی مانند نام صاحب حساب و موجودی حساب داشته باشد.

اضافه کنید متدهای برداشت و واریز برای انجام عملیات بانکی.

تمرین:

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

 

 

3. کلاس حسابداری

یک کلاس با نام حسابداری بسازید که لیستی از صورتحساب‌ها را ذخیره کند.

اضافه کنید یک متد برای محاسبه مجموع صورتحساب‌ها.

تمرین:

یک شیء از کلاس حسابداری بسازید و مجموع صورتحساب‌ها را محاسبه کنید.

 

 

4. کلاس فروشگاه

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

اضافه کنید یک متد برای محاسبه تخفیف که تخفیف درصدی را بر قیمت محصول اعمال کند.

تمرین:

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

 

 

5. کلاس رستوران

یک کلاس با نام رستوران* بسازید که ویژگی‌هایی مانند نام غذا و قیمت را داشته باشد.

اضافه کنید یک متد برای حساب کردن صورت‌حسابکه مجموع قیمت غذاها را محاسبه کند.

تمرین:

یک شیء از کلاس رستوران بسازید و صورت‌حساب غذاهای آن را محاسبه کنید.

 

تمرین سطح مبتدی و پیشرفته

در اینجا 5 تمرین خلاقانه و کمی سخت‌تر برای یادگیری شی‌گرایی در پایتون آورده شده است که به دانش‌آموزان کمک می‌کند تا با مفاهیم پیشرفته‌تری از شی‌گرایی آشنا شوند:

 

1. سیستم مدیریت مدرسه

یک سیستم مدیریت مدرسه طراحی کنید که شامل چند کلاس باشد:

- کلاس دانش‌آموز: ویژگی‌های نام، سن، کلاس تحصیلی و نمرات.

- کلاس معلم: ویژگی‌های نام، موضوع تدریس و کلاس‌های تدریس‌شده.

- کلاس کلاس تحصیلی: ویژگی‌های نام کلاس، لیست دانش‌آموزان و لیست معلمان.

 

اضافه کنید متدهایی برای:

- افزودن دانش‌آموز به یک کلاس.

- افزودن معلم به یک کلاس.

- محاسبه میانگین نمرات دانش‌آموزان در یک کلاس.

 

تمرین:

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

 

 

2. سیستم رزرو هتل

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

- کلاس اتاق: ویژگی‌های نوع اتاق (یک‌نفره، دو‌نفره، سوئیت)، قیمت و وضعیت رزرو (رزرو شده یا خالی).

- کلاس مهمان: ویژگی‌های نام، تاریخ ورود و تاریخ خروج.

- کلاس رزرو: ویژگی‌های مهمان، اتاق رزرو شده و تاریخ رزرو.

 

اضافه کنید متدهایی برای:

- رزرو اتاق.

- لغو رزرو.

- نمایش وضعیت اتاق‌ها (آیا اتاق خالی است یا رزرو شده).

 

تمرین:

یک شیء از کلاس رزرو بسازید و از آن برای رزرو و لغو اتاق‌ها استفاده کنید. همچنین وضعیت اتاق‌ها را به‌روزرسانی کنید.

 

 

3. سیستم فروشگاه آنلاین

یک سیستم فروشگاه آنلاین طراحی کنید که شامل چند کلاس باشد:

- کلاس محصول: ویژگی‌های نام محصول، قیمت، تعداد موجودی.

- کلاس سبد خرید: ویژگی‌های لیست محصولات و جمع قیمت.

- کلاس مشتری: ویژگی‌های نام مشتری، آدرس ارسال، سبد خرید (اشیاء از کلاس سبد خرید).

 

اضافه کنید متدهایی برای:

- افزودن محصول به سبد خرید.

- حذف محصول از سبد خرید.

- پرداخت و نمایش فاکتور.

 

تمرین:

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

 

 

4. سیستم مدیریت کتابخانه

یک سیستم مدیریت کتابخانه طراحی کنید که شامل چند کلاس باشد:

- کلاس کتاب: ویژگی‌های عنوان، نویسنده، وضعیت (موجود یا قرضی).

- کلاس کاربر: ویژگی‌های نام کاربر، لیست کتاب‌های قرضی.

- کلاس کتابخانه: ویژگی‌های لیست کتاب‌ها و لیست کاربران.

 

اضافه کنید متدهایی برای:

- افزودن کتاب به کتابخانه.

- قرض دادن کتاب به کاربر.

- برگرداندن کتاب به کتابخانه.

 

تمرین:

یک سیستم کتابخانه با چند کاربر و کتاب ایجاد کنید و قابلیت قرض دادن و برگرداندن کتاب‌ها را پیاده‌سازی کنید.

 

 

 

5. سیستم بازی تخته‌ای

یک سیستم برای یک بازی تخته‌ای مانند شطرنج یا دومینو طراحی کنید که شامل چند کلاس باشد:

- کلاس بازیکن: ویژگی‌های نام بازیکن، امتیاز.

- کلاس مهره‌ها: ویژگی‌های نام مهره و موقعیت.

- کلاس بازی: ویژگی‌های بازیکنان، مهره‌ها و نوبت بازی.

 

اضافه کنید متدهایی برای:

- شروع بازی.

- حرکت مهره‌ها.

- برنده شدن (هر بازیکن پس از بردن بازی، امتیاز می‌گیرد).

تمرین:

یک شیء از کلاس بازی بسازید و بازی را بین دو بازیکن شروع کنید. مهره‌ها را حرکت دهید و وضعیت بازی را پیگیری کنید.

 

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