~/icsd.ir — bash
SYSTEM_ONLINE

برنامه‌نویسی شیءگرا (مقدمه)

برنامه‌نویسی شیءگرا (OOP) روشی برای سازمان‌دهی کد بر اساس «اشیاء» است. در این فصل با کلاس‌ها، اشیاء و مفاهیم پایه OOP در پایتون آشنا می‌شویم.

برنامه‌نویسی شیءگرا (OOP) روشی برای سازمان‌دهی کد بر اساس «اشیاء» است. در این فصل با کلاس‌ها، اشیاء و مفاهیم پایه OOP در پایتون آشنا می‌شویم.

چرا OOP؟

# بدون OOP - مدیریت چند کاربر سخت می‌شود
user1_name = "علی"
user1_age = 25
user1_email = "ali@example.com"

user2_name = "زهرا"
user2_age = 30
user2_email = "zahra@example.com"

# با OOP - تمیز و قابل گسترش
class User:
    def __init__(self, name, age, email):
        self.name = name
        self.age = age
        self.email = email
    
    def greet(self):
        return f"سلام، من {self.name} هستم"

user1 = User("علی", 25, "ali@example.com")
user2 = User("زهرا", 30, "zahra@example.com")

print(user1.greet())
print(user2.greet())

تعریف کلاس

class Dog:
    """کلاسی برای نمایش سگ"""
    
    def __init__(self, name, breed, age):
        """سازنده - وقتی شیء جدید ساخته می‌شود اجرا می‌شود"""
        self.name = name
        self.breed = breed
        self.age = age
    
    def bark(self):
        """متد - رفتار شیء"""
        return f"{self.name} می‌گوید: واق واق!"
    
    def info(self):
        return f"{self.name} ({self.breed}, {self.age} ساله)"

# ساخت شیء (instance)
my_dog = Dog("رکس", "ژرمن شپرد", 3)

# دسترسی به ویژگی‌ها
print(my_dog.name)        # رکس
print(my_dog.age)         # 3

# فراخوانی متدها
print(my_dog.bark())      # رکس می‌گوید: واق واق!
print(my_dog.info())      # رکس (ژرمن شپرد, 3 ساله)

پارامتر self

self به شیء جاری اشاره می‌کند. اولین پارامتر هر متد است:

class Counter:
    def __init__(self):
        self.count = 0    # ویژگی شیء
    
    def increment(self):
        self.count += 1    # تغییر روی همین شیء
    
    def reset(self):
        self.count = 0
    
    def get(self):
        return self.count

c1 = Counter()
c2 = Counter()

c1.increment()
c1.increment()
c1.increment()
c2.increment()

print(c1.get())     # 3
print(c2.get())     # 1   (اشیاء مستقل!)

متد __init__

سازنده (Constructor) – هنگام ساخت شیء فراخوانی می‌شود:

class Product:
    def __init__(self, name, price, stock=0):
        # پارامترها → ویژگی‌های شیء
        self.name = name
        self.price = price
        self.stock = stock
        
        # ویژگی‌های محاسبه‌شده
        self.tax = price * 0.09
        self.total = price + self.tax
    
    def display(self):
        return f"{self.name}: {self.price:,} تومان (موجودی: {self.stock})"

laptop = Product("لپ‌تاپ", 50_000_000, stock=5)
mouse = Product("ماوس", 500_000)    # stock پیش‌فرض 0

print(laptop.display())
print(mouse.display())
print(f"مالیات لپ‌تاپ: {laptop.tax:,.0f}")

ویژگی‌های کلاس و شیء

class Employee:
    # ویژگی کلاس - بین همه اشیاء مشترک
    company = "ICSD"
    raise_rate = 1.05
    
    def __init__(self, name, salary):
        # ویژگی شیء - برای هر کس متفاوت
        self.name = name
        self.salary = salary
    
    def apply_raise(self):
        self.salary = int(self.salary * Employee.raise_rate)

emp1 = Employee("علی", 20_000_000)
emp2 = Employee("زهرا", 25_000_000)

print(emp1.company)         # ICSD
print(emp2.company)         # ICSD  (مشترک)

# تغییر در سطح کلاس → روی همه اثر می‌گذارد
Employee.company = "ICSD International"
print(emp1.company)         # ICSD International
print(emp2.company)         # ICSD International

emp1.apply_raise()
print(emp1.salary)          # 21_000_000
print(emp2.salary)          # 25_000_000  (تغییر نکرد)

انواع متد

Instance Method (روش معمول)

class Circle:
    def __init__(self, radius):
        self.radius = radius
    
    # متد نمونه - با self
    def area(self):
        return 3.14159 * self.radius ** 2
    
    def perimeter(self):
        return 2 * 3.14159 * self.radius

c = Circle(5)
print(c.area())          # 78.53975

Class Method – با @classmethod

class Person:
    population = 0    # ویژگی کلاس
    
    def __init__(self, name):
        self.name = name
        Person.population += 1
    
    @classmethod
    def get_population(cls):
        # cls اشاره به کلاس، نه شیء
        return cls.population
    
    @classmethod
    def from_string(cls, data):
        # سازنده جایگزین
        name = data.split(",")[0]
        return cls(name)

# فراخوانی روی کلاس (نه شیء!)
p1 = Person("علی")
p2 = Person("زهرا")
print(Person.get_population())     # 2

# سازنده جایگزین
p3 = Person.from_string("محمد,30")
print(p3.name)                      # محمد

Static Method – با @staticmethod

class MathHelper:
    @staticmethod
    def is_even(n):
        # نه self، نه cls - تابع معمولی داخل کلاس
        return n % 2 == 0
    
    @staticmethod
    def fa_to_en(text):
        fa = "۰۱۲۳۴۵۶۷۸۹"
        en = "0123456789"
        return text.translate(str.maketrans(fa, en))

# می‌توان روی کلاس فراخوانی کرد
print(MathHelper.is_even(4))    # True
print(MathHelper.fa_to_en("۱۴۰۴"))    # 1404

کپسوله‌سازی

پایتون اعضای واقعاً private ندارد، اما قراردادی دارد:

class Account:
    def __init__(self, owner, balance):
        self.owner = owner            # عمومی
        self._balance = balance       # محافظت شده (قراردادی)
        self.__pin = "1234"           # private (name mangling)
    
    def get_balance(self):
        return self._balance
    
    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
    
    def __validate_pin(self, pin):
        # متد private
        return pin == self.__pin

acc = Account("علی", 1000000)

# دسترسی عمومی
print(acc.owner)            # علی

# با _ - فنی ممکن، عرفی نه
print(acc._balance)         # 1000000

# با __ - واقعاً پنهان (name mangling)
# print(acc.__pin)          # AttributeError
print(acc._Account__pin)    # 1234  (هنوز قابل دسترسی)

@property

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

class Temperature:
    def __init__(self, celsius):
        self._celsius = celsius
    
    @property
    def celsius(self):
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("کمتر از صفر مطلق ممکن نیست")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        # محاسبه‌شده
        return self._celsius * 9/5 + 32

t = Temperature(25)
print(t.celsius)       # 25      (مثل ویژگی، نه متد!)
print(t.fahrenheit)    # 77.0    (محاسبه‌شده)

t.celsius = 30         # setter اجرا می‌شود
print(t.fahrenheit)    # 86.0

# t.celsius = -300     # ValueError

متدهای جادویی (Dunder)

متدهای ویژه با __ در دو طرف:

class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages
    
    def __str__(self):
        # برای print()
        return f"{self.title} - {self.author}"
    
    def __repr__(self):
        # برای دیباگ و REPL
        return f"Book(title={self.title!r}, pages={self.pages})"
    
    def __len__(self):
        # برای len()
        return self.pages
    
    def __eq__(self, other):
        # برای ==
        if not isinstance(other, Book):
            return False
        return self.title == other.title
    
    def __lt__(self, other):
        # برای < (کم‌تر از)
        return self.pages < other.pages

b1 = Book("پایتون", "علی", 300)
b2 = Book("جنگ و صلح", "تالستوی", 1200)

print(b1)           # پایتون - علی        (__str__)
print(repr(b1))     # Book(title='پایتون', pages=300)  (__repr__)
print(len(b1))      # 300                  (__len__)
print(b1 == b2)     # False                (__eq__)
print(b1 < b2)      # True                 (__lt__)

# حالا می‌توان مرتب کرد
books = [b1, b2]
books.sort()        # از __lt__ استفاده می‌کند

پیش‌نمای ارث‌بری

(در دوره پیشرفته مفصل بحث می‌شود)

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return "صدا می‌کند"

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

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

animals = [Dog("رکس"), Cat("پیشی")]
for a in animals:
    print(a.speak())

مثال جامع: مدیر کارها

from datetime import datetime

class Task:
    next_id = 1
    
    def __init__(self, title, priority="medium"):
        self.id = Task.next_id
        Task.next_id += 1
        self.title = title
        self.priority = priority
        self.done = False
        self.created_at = datetime.now()
        self.completed_at = None
    
    def complete(self):
        self.done = True
        self.completed_at = datetime.now()
    
    @property
    def status(self):
        return "✅" if self.done else "⏳"
    
    def __str__(self):
        return f"{self.status} #{self.id}: {self.title} [{self.priority}]"
    
    def __repr__(self):
        return f"Task({self.title!r})"

class TaskManager:
    def __init__(self):
        self.tasks = []
    
    def add(self, title, priority="medium"):
        task = Task(title, priority)
        self.tasks.append(task)
        return task
    
    def complete(self, task_id):
        for task in self.tasks:
            if task.id == task_id:
                task.complete()
                return True
        return False
    
    def list_pending(self):
        return [t for t in self.tasks if not t.done]
    
    @property
    def total(self):
        return len(self.tasks)
    
    @property
    def completed_count(self):
        return sum(1 for t in self.tasks if t.done)

# استفاده
tm = TaskManager()
tm.add("یادگیری OOP", "high")
tm.add("نوشتن تمرین")
tm.add("مرور فصل قبل", "low")

tm.complete(1)

print(f"کل: {tm.total}, تکمیل‌شده: {tm.completed_count}")
for task in tm.tasks:
    print(task)

تمرین فصل

🏋️ تمرین ۱: کلاس Rectangle با ویژگی‌های width و height و متدهای area و perimeter بسازید.
🏋️ تمرین ۲: کلاس BankAccount با متدهای deposit، withdraw و get_balance بسازید. در صورت برداشت بیش از موجودی، خطا بدهد.
🏋️ تمرین ۳: کلاس Student با لیست نمرات بسازید. متد average برای میانگین و @property grade برای حرف نمره (A, B, …).
🏋️ تمرین ۴: کلاس Vector با عملگرهای __add__، __sub__ و __str__ بنویسید (اعداد دو بعدی).

جمع‌بندی

  • کلاس و شیء (Object) – مفاهیم پایه OOP
  • متد __init__ به‌عنوان سازنده
  • پارامتر self اشاره به شیء جاری
  • تفاوت ویژگی کلاس و ویژگی شیء
  • سه نوع متد: instance، class، static
  • کپسوله‌سازی با _ و __
  • @property برای getter و setter پایتونیک
  • متدهای dunder: __str__، __eq__، __len__، …

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.