برنامهنویسی شیءگرا (مقدمه)
برنامهنویسی شیءگرا (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__، …