🚀 فصل ۱۱: شیگرایی (Object-Oriented Programming) در پایتون

۱۱.۱ | شیگرایی یعنی چی؟ (مقدمهای ساده)
برنامهنویسی شیگرا روشی برای نگهداری و مدیریت کد است که همهچیز حول محور “اشیاء” (Object) سازماندهی میشود، نه فقط توابع و متغیرها.
در شیگرایی، شما میتونید چیزهای واقعی (مثل ماشین، کاربر، حساب بانکی)، یا حتی مفاهیم انتزاعیتر (تراکنش، پیام، سفارش) رو بهصورت یک شی مدل کنید.
هر شی دو بخش اصلی داره:
- ویژگیها (attributes – دادههای شی)
- رفتارها (methods – کارهایی که شی میتونه انجام بده – در واقع همون تابع داخل شی)
۱۱.۲ | کلاس و شی چیست؟ تفاوت کلاس و شی
- کلاس (class): قالب یا نقشه برای ساختن شیهاست.
- شی (object): نمونهی واقعی ساختهشده از یک کلاس.
مثال ساده:
کلاس = نقشهی یک خانه
شی = یک خانه واقعی که روی زمین ساخته شده
۱۱.۳ | چگونه کلاس بسازیم؟
تعریف یک کلاس ساده:
class Car:
pass # وقتی هنوز نمیخواید چیزی داخل کلاس بنویسید (کلمه ی pass یعنی هیچ کاری نکن!)
ساختن یک شی از روی کلاس:
my_car = Car()
print(type(my_car))
خروجی:
<class '__main__.Car'>
۱۱.۴ | سازنده کلاس – متد init
وقتی یک شی ساخته میشود، متد مخصوص به نام __init__ (سازنده) خودش اتومات صدا زده میشه تا ویژگیهای اولیه رو بده.
مثال:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
my_dog = Dog("ماکسی", 3)
print(my_dog.name) # خروجی: ماکسی
print(my_dog.age) # خروجی: 3
هر متدی که تو کلاس تعریف میکنی باید اولین پارامترش self باشه (به خودش یعنی شی فعلی اشاره میکنه).
۱۱.۵ | ویژگیها (attributes) و رفتارها (methods)
اضافه کردن ویژگی (Attribute)
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
my_book = Book("شازده کوچولو", "اکسیوپری")
print(my_book.title) # خروجی: شازده کوچولو
تعریف رفتار یا متد (Method)
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def show_info(self):
print(f"{self.title} نوشتهی {self.author}")
b = Book("شازده کوچولو", "اکسیوپری")
b.show_info() # خروجی: شازده کوچولو نوشتهی اکسیوپری
۱۱.۶ | تفاوت ویژگیهای نمونه و ویژگیهای کلاس
- ویژگی نمونه (Instance Attribute): وابسته به هر شی (self)
- ویژگی کلاس (Class Attribute): مشترک برای همه اشیا (خارج از def init)
مثال:
class Cat:
species = "پستاندار" # ویژگی کلاس
def __init__(self, name):
self.name = name # ویژگی نمونه
cat1 = Cat("سیاه")
cat2 = Cat("سفید")
print(cat1.species) # خروجی: پستاندار
print(cat2.species) # خروجی: پستاندار
print(cat1.name) # خروجی: سیاه
print(cat2.name) # خروجی: سفید
۱۱.۷ | متدهای ویژه (Special Methods) – مانند str
- متد
__str__روش چاپ شی رو تعیین میکنه:
class Book:
def __init__(self, title):
self.title = title
def __str__(self):
return f"کتاب: {self.title}"
b = Book("کیمیاگر")
print(b) # خروجی: کتاب: کیمیاگر
۱۱.۸ | وراثت (Inheritance) – استفاده از قابلیت یک کلاس در کلاس دیگر
گاهی میخوایم یک کلاس جدید بسازیم که تمام ویژگیها و رفتارهای کلاس قبلی رو داشته باشه و فقط کمی فرق کنه.
مثال:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("صدا ندارد!")
class Cat(Animal):
def speak(self):
print("میو میو")
cat = Cat("ملوس")
cat.speak() # خروجی: میو میو
کلاس Cat هم ویژگی name رو داره (از Animal به ارث برده)، هم متد speak رو بازنویسی (override) کرده.
حتماً! کپسولهسازی یکی از ستونهای اصلی شیگرایی هست و اگر بهدرستی متوجهش بشی، برنامههات هم ساختاریتر، هم امنتر و هم حرفهایتر میشه.
در این مقاله با مثالهای واقعی، نکات ظریف و کاربردهاش رو خیلی ساده، گامبهگام و کامل بررسی میکنیم.
کپسولهسازی (Encapsulation) در پایتون: راهنمای کامل و کاربردی
💡 کپسولهسازی یعنی چی؟
کپسولهسازی (Encapsulation) یعنی:
- مخفی کردن جزئیات داخلی شی (کلاس) تا هر کسی نتونه از بیرون به دادهها (متغیرها) و متدهای داخلی دسترسی مستقیم داشته باشه.
- به زبان ساده یعنی یک “پوسته” دور دادهها و توابع کلاس میکشی تا کنترل کنی چه کسی و چطور بهشون دسترسی داشته باشه.
این کار باعث:
- امنیت بیشتر (اطلاعات مهم مثل پسورد، موجودی بانک و …)
- جلوگیری از بهمریختگی و اشتباهات ناخواسته
- امکان تغییر دیتای داخلی بدون نگرانی برای دیگران
- سادگی در استفاده (دیگران میفهمن چه چیزی در دسترسِشونه، چه چیزی نه)
📦 چطور در پایتون کپسولهسازی کنیم؟
در برخی زبانها مثل Java یا C# کلمه کلیدی داریم مثل private یا public.
اما در پایتون راهش فرق داره:
اینجا روی فرهنگ برنامهنویسی و نشانهگذاری (Name Mangling) حساب میشه، نه اجبار سختگیرانه.
سه حالت برای متغیرهای کلاس:
- عمومی (Public): همه جا قابل دسترس
python self.name = value - محافظتشده (Protected): توصیه میشه فقط در خود کلاس و زیرکلاسها استفاده بشه
python self._age = value - خصوصی (Private): فقط داخل خود کلاس
python self.__balance = value
۱. حالت عمومی (Public)
class Person:
def __init__(self, name):
self.name = name # عمومی
p = Person("علی")
print(p.name) # بله! دسترسی داریم
۲. حالت محافظتشده (Protected) با یک آندرلاین
قانونی نیست که کسی نتونه دسترسی پیدا کنه؛ فقط یک «لطفا دست نزن» حرفهایه!
class Person:
def __init__(self, name):
self._name = name # محافظتشده (نه خصوصی!)
p = Person("علی")
print(p._name) # قابل دسترسی، ولی توصیه نمیشه
۳. حالت خصوصی (Private) با دو آندرلاین
پایتون خودش اسم متغیر رو تغییر میده! این یعنی دسترسی مستقیم سختتر (اما باز هم هکشدنیه).
class Person:
def __init__(self, name):
self.__name = name # خصوصی
p = Person("علی")
# print(p.__name) # خطا -> AttributeError
اگر سعی کنی مستقیم دسترسی پیدا کنی خطا میگیری، اما:
print(p._Person__name) # بهش دسترسی داری! اما اصلاً توصیه نمیشه
این مکانیزم رو Name Mangling میگن.
✨ چرا کپسولهسازی؟ با مثال واقعی
فرض کن حساب بانکی داری و مقدار موجودی داخلش هست:
class BankAccount:
def __init__(self, balance):
self.__balance = balance
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def withdraw(self, amount):
if amount > 0 and amount <= self.__balance:
self.__balance -= amount
def get_balance(self):
return self.__balance
از بیرون، معلوم نیست موجودی حساب چقدره و کسی مستقیماً نمیتونه تغییرش بده:
acc = BankAccount(1000)
acc.deposit(500)
print(acc.get_balance()) # 1500
acc.__balance = 1_000_000 #!!! فکر میکنیم مقدار تغییر کرد، اما واقعاً نه!
print(acc.get_balance()) # هنوز همون مقدار اصلی (1500)
print(acc.__dict__)
# خروجی میبینی که __balance واقعاً عوض نشده؛ یه متغیر جدید با این اسم ساخته شد!
🛡️ دسترسیها را کنترل کن: Getter و Setter
بهترین روش اینه که دسترسی به اطلاعات رو فقط از طریق “متدهای کنترل شده” (getter و setter) فراهم کنی.
نمونه ساده:
class Student:
def __init__(self, score):
self.__score = score
def get_score(self): # Getter
return self.__score
def set_score(self, value): # Setter
if 0 <= value <= 20:
self.__score = value
else:
print("نمره نامعتبر است!")
stu = Student(17)
print(stu.get_score())
stu.set_score(21) # نمره نامعتبر است!
print(stu.get_score()) # 17
🏷️ property: کپسولسازی پیشرفته و شیواتر
پایتون راهی شیک داره:
میتونی با دکوریتور @property متد getter و setter بسازی و استفاده ازش درست مثل یک ویژگی عادی باشه!
class Product:
def __init__(self, price):
self.__price = price
@property
def price(self): # Getter
return self.__price
@price.setter
def price(self, value): # Setter
if value >= 0:
self.__price = value
else:
print("قیمت نمیتونه منفی باشه!")
p = Product(250)
print(p.price) # مثل ویژگی عادی
p.price = -5 # قیمت نمیتونه منفی باشه!
print(p.price) # 250
خاصیت property باعث میشه بدون اینکه درگیر متد بشی، انگار با یک متغیر معمولی کار کنی اما پشت پرده همهچیز حرفهای و امن بمونه!
🚦 کپسولهسازی و وراثت: در عمل چه اتفاقی میافتد؟
اگر متغیری خصوصی تعریف کردی (__var)، در زیرکلاس (SubClass) بهش دسترسی نداری! اما متغیر محافظتشده (_var) در زیرکلاس قابل استفادهست.
مثال:
class Parent:
def __init__(self):
self._protected = "yes"
self.__private = "نه"
class Child(Parent):
def show(self):
print(self._protected) # اوکی
# print(self.__private) # خطا میده
c = Child()
c.show()
📝 نکتههای حرفهای کپسولهسازی در پایتون
- استفاده از دو آندرلاین واقعاً حالت خصوصی میسازه، اما هکر یا برنامهنویس پیشرفته در مواقع خاص باز هم راههایی داره.
- Propertyها رو برای خواناتر کردن کد، سادهتر کردن و کنترل بهتر توصیه اکید میکنم.
- قوانین کپسولهسازی و “عدم دسترسی” اخلاقیتر از فنی است (Python به تو اعتماد دارد ولی انتظار دارد اشتباه نکنی!).
- بهترین راه: “دسترسی فقط از طریق متد!”
💪 جمعبندی: چراEncapsulation مهم است؟
- ساختار کد را تمیزتر نگه میدارد
- امنیت دادههای حساس را بیشتر میکند
- کد قابل تغییرتر (Maintainable) و قابل گسترشتر میشود
- اشتباهات ناخواسته را کاهش میدهد
- برای پروژههای تیمی و سیستمهای بزرگ حیاتی است
تمرین سریع
۱. یک کلاس بساز که مقدار نمره (0 تا 100) را فقط با property قابل تغییر کند و اگر کسی عدد اشتباهی داد، خطا نمایش دهد.
۲. تست کن: اگر کسی از بیرون بخواهد مقدار خصوصی را عوض کند، چه اتفاقی میافتد؟
۱۱.۱۰ | چندشکلی (Polymorphism) و متد override
- چندشکلی = یک متد میتونه اعمال مختلف بر کلاسهای مختلف انجام بده.
- override = بازنویسی یک متد که از کلاس پدر به ارث بردهاید.
مثال ساده:
class Animal:
def speak(self):
print("صدا ندارد!")
class Dog(Animal):
def speak(self):
print("هاپ هاپ")
class Cat(Animal):
def speak(self):
print("میو میو")
animals = [Dog(), Cat(), Animal()]
for a in animals:
a.speak()
# خروجی:
# هاپ هاپ
# میو میو
# صدا ندارد!
۱۱.۱۱ | جمعبندی سریع با جدول
| اصطلاح | معنی و مثال کوتاه |
|---|---|
| کلاس (Class) | قالب یا نقشه ساخت اشیا (class Car:) |
| شی (Object) | نمونه از کلاس (my_car = Car()) |
| ویژگی (Attribute) | دادههای هر شی (self.name = name) |
| متد (Method) | رفتاری که شی میتواند انجام دهد (def drive(self)) |
| وراثت (Inheritance) | استفاده از یک کلاس دیگر (class Cat(Animal)) |
| کپسولهسازی (Encapsulation) | مخفیکردن داده (متغیر خصوصی) |
| چندشکلی (Polymorphism) | یک متد چند رفتار مختلف دارد |
۱۱.۱۲ | تمرینات ساده و کاربردی (OOP)
۱. کلاسی به اسم Student بساز که نام، سن و نمره داشته باشد و یک متد show_info نمایش اطلاعات بنویسد.
۲. کلاسی به اسم Circle بساز که شعاع را بگیرد، متدی برای محاسبه مساحت و محیط دایره بنویسد (فرمول: مساحت=πr^2، محیط=2πr).
۳. وراثت ایجاد کن: کلاس Vehicle (خودرو)، کلاس Car از Vehicle ارث ببرد و یک متد منحصر به فرد به Car اضافه کن (مثلاً: play_music()).
۱۱.۱۳ | TIP: توابع در کلاسها – self دقیقاً یعنی چه؟
self همیشه به “همین شی فعلی” اشاره داره، وقتی مینویسی self.name یعنی این مقدار متعلق به همین نمونه است.
مثلاً:
d1 = Dog("لوسی", 3)
d2 = Dog("هاپو", 2)
# هر دو شی ویژگیهای خودشون رو دارن، با self بهشون دسترسی داریم.