شیگرایی (OOP) در پایتون
جلسه یازدهم - شیگرایی (OOP) در پایتون
آموزش کامل برنامهنویسی شیگرا در پایتون
شیگرایی چیه؟
شیگرایی (Object Oriented Programming - OOP) یه روش برنامهنویسیه که به جای تمرکز روی توابع و دادههای جدا از هم، روی اشیاء (objects) تمرکز داره. هر شی خودش یه موجود مستقل با ویژگیها (attributes) و رفتارها (methods) هست. مثلاً توی برنامهای که درباره ماشینهاست، هر ماشین میتونه یه شی باشه با ویژگیهایی مثل رنگ، مدل و رفتارهایی مثل حرکت یا ترمز.
هدف شیگرایی
- خواناتر، منظمتر و مقیاسپذیر کردن برنامهها
- استفاده مجدد از کد (reuse)
- جدا کردن بخشها و افزایش امنیت/کپسولهسازی
- سهولت در مدیریت پروژههای بزرگ
ساختار کلی در پایتون
در پایتون با استفاده از class یه الگو (قالب) میسازیم و از روش
آن، اشیا ساخته میشن.
class Car:
def __init__(self, brand, color):
self.brand = brand
self.color = color
def start(self):
print(f"{self.brand} is starting...")
car1 = Car("BMW", "Black")
car1.start()
توضیح: class برای تعریف کلاس، __init__ متد سازنده، و self اشاره
به خود شی دارد. car1.start() متد کلاس را اجرا میکند.
__init__ و self
__init__ متد سازنده است که هنگام ساخت شی اجرا میشود؛ و self مرجع آن شی است.
class Car:
def __init__(self, brand):
self.brand = brand
def start(self):
print(f"{self.brand} is starting")
car = Car("Toyota")
car.start()
نکته: اسم self یک قرارداد است؛ میتوان نام
دیگری گذاشت اما رعایت self خوانایی کد را افزایش میدهد.
Attributes — صفتهای نمونه و کلاس
ویژگیها (attributes) میتوانند به صورت صفت نمونه (instance attribute) یا صفت کلاس (class attribute) تعریف شوند.
class Car:
wheels = 4 # class attribute
def __init__(self, brand):
self.brand = brand # instance attribute
c1 = Car("BMW")
c2 = Car("Toyota")
print(c1.wheels, c2.wheels)
Car.wheels = 3
print(c1.wheels, c2.wheels)
c1.wheels = 6
print(c1.wheels, c2.wheels)
توضیح: صفات کلاس برای همه نمونهها مشترکند؛ اما اگر از روی نمونه مقداری برای همان نام تعیین شود، آن نمونه یک صفت نمونه جدید خواهد داشت.
__repr__ و __str__
متد __repr__ نمایش دقیقتر و قابل بازسازی شی است؛ __str__ نمایش user-friendly.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Person('{self.name}', {self.age})"
def __str__(self):
return f"{self.name}, Age: {self.age}"
p = Person('Fatemeh', 21)
print(p)
print(repr(p))
خروجی نمونه:
Fatemeh, Age: 21
Person('Fatemeh', 21)
نکته: اگر __str__ تعریف نشود، print() از __repr__ استفاده
میکند.
متدهای جادویی (Operator Overloading)
متدهایی مثل __add__, __sub__, __mul__ برای تعریف رفتار عملگرها روی اشیاء استفاده میشن.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(3,5)
v2 = Vector(1,2)
v3 = v1 + v2
print(v3.x, v3.y)
نکته: همیشه شی جدید برگردانید و حالت داخلی شی فعلی را تغییر ندهید مگر به عمد.
متدهای تبدیل نوع: __int__ ، __float__
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
def __int__(self):
return int(self.celsius)
def __float__(self):
return float(self.celsius)
t = Temperature(36.6)
print(int(t))
print(float(t))
نکته: این متدها باید خروجی عددی برگردانند؛ توابع داخلی int() و float() آنها را فراخوانی
میکنند.
آشنایی با @classmethod ، @staticmethod و @property
class DateUtils:
@staticmethod
def is_leap_year(year):
return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
print(DateUtils.is_leap_year(2024))
class Circle:
def __init__(self, r):
self._r = r
@property
def area(self):
return 3.14 * self._r ** 2
@property
def radius(self):
return self._r
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("Radius must be positive")
self._r = value
c = Circle(5)
print(c.area)
c.radius = 10
print(c.area)
نکته: @staticmethod برای تابع مستقل، @classmethod برای رفتار مرتبط با کلاس (پارامتر cls) و @property برای تبدیل متد به
ویژگی استفاده میشه.
وراثت (Inheritance)
وراثت به شما اجازه میده یک کلاس جدید بسازید که از کلاس دیگری ویژگیها و متدها را به ارث ببرد.
class Parent:
def greet(self):
print("Hello from Parent")
class Child(Parent):
def greet(self):
super().greet()
print("Hello from Child")
c = Child()
c.greet()
نکته: با super() میتوانید متد والد را
فراخوانی کنید تا رفتار والد نیز اجرا شود.
چندریختی (Polymorphism)
چندریختی یعنی چند کلاس میتونن متدهایی با یک نام داشته باشند اما رفتار متفاوت ارائه بدن.
class Bird:
def sound(self):
print("Chirp!")
class Cat:
def sound(self):
print("Meow!")
for animal in [Bird(), Cat()]:
animal.sound()
نکته: نوع شی مهم نیست؛ فقط متد مورد نظر را فراخوانی میکنیم.
انتزاع (Abstraction) با کلاسهای انتزاعی (ABC)
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return 3.14 * self.r * self.r
c = Circle(5)
print(c.area())
نکته: اگر کلاس فرزند متد انتزاعی را پیادهسازی نکنه، خطا رخ میده؛ ABCها برای تعریف رابط (interface) مفیدن.
کپسولهسازی (Encapsulation)
کپسولهسازی یعنی دادههای داخلی کلاس را مخفی کنیم و تنها با متدهای مشخص به آنها دسترسی دهیم.
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 get_balance(self):
return self.__balance
acc = BankAccount(1000)
acc.deposit(500)
print(acc.get_balance())
نکته: ویژگیهایی که با دو آندرلاین شروع میشن (مثل __balance) توسط نامگذاری مَنگلینگ خصوصی میشن و دسترسی مستقیم
از بیرون را دشوار میکنند.
مثالهای ترکیبی
مثال: کلاس Temperature با @property ، @staticmethod و @classmethod
class Temperature:
count = 0
def __init__(self, celsius):
self.celsius = celsius
Temperature.count += 1
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Temperature cannot go below absolute zero!")
self._celsius = value
@staticmethod
def to_fahrenheit(celsius):
return (celsius * 9/5) + 32
@classmethod
def total_created(cls):
return f"Total temperature objects: {cls.count}"
# استفاده
x = Temperature(25)
print(x.to_fahrenheit(x.celsius))
print(Temperature.total_created())
تحلیل: ترکیب دکوریتورها به شما امکان میده ویژگیها را کنترل، تبدیلها را به عنوان متدهای کمکی قرار داده و اطلاعات سطح کلاس را نگهداری کنید.
جمعبندی
- OOP باعث خوانایی، سازماندهی و قابلیت توسعهٔ برنامهها میشود.
- اصول اصلی: Encapsulation، Inheritance، Polymorphism، Abstraction
- متدهای جادویی رفتار عملگرها را روی اشیاء مشخص میکنند.
- دکوریتورها (@property, @staticmethod, @classmethod) ابزارهای قدرتمندی برای طراحی کلاس هستند.
💡 تمرینها و سوالات
سوال 1: ساخت کلاس ساده برای دانشجو
کلاسی به نام Student بسازید که نام و نمره دانشجو را نگه دارد و متدی
برای نمایش اطلاعات داشته باشد.
# خروجی: Student: علی, Score: 18
# راه حل:
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def display(self):
print(f"Student: {self.name}, Score: {self.score}")
s1 = Student("علی", 18)
s1.display()
نکته آموزشی: این سادهترین فرم کلاس است. __init__ برای مقداردهی اولیه و متدها برای رفتارهای شی استفاده
میشن.
سوال 2: کلاس با متد محاسباتی
کلاس Rectangle بسازید که طول و عرض بگیرد و مساحت و محیط را محاسبه
کند.
# محاسبه مساحت و محیط مستطیل
# راه حل:
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(f"Area: {rect.area()}") # 15
print(f"Perimeter: {rect.perimeter()}") # 16
نکته آموزشی: متدها میتوانند محاسبات را انجام دهند و نتیجه برگردانند. هر متد
به ویژگیهای شی با self دسترسی دارد.
سوال 3: استفاده از __str__ برای نمایش خوانا
کلاس Book بسازید که عنوان، نویسنده و سال انتشار را ذخیره کند و با
print() به شکل خوانا نمایش دهد.
# خروجی: "کتاب 1984 نوشته جورج اورول (1949)"
# راه حل:
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.year = year
def __str__(self):
return f"کتاب {self.title} نوشته {self.author} ({self.year})"
book = Book("1984", "جورج اورول", 1949)
print(book)
نکته آموزشی: متد __str__ برای نمایش
user-friendly شی استفاده میشه. وقتی print() صدا زده میشه، این
متد اجرا میشود.
سوال 4: صفت کلاس برای شمارش اشیاء
کلاس Car بسازید که تعداد کل ماشینهای ساخته شده را بشمارد.
# هر بار که ماشین جدید ساخته میشه، شمارنده افزایش یابد
# راه حل:
class Car:
total_cars = 0 # صفت کلاس
def __init__(self, brand):
self.brand = brand
Car.total_cars += 1
@classmethod
def get_total(cls):
return f"Total cars created: {cls.total_cars}"
car1 = Car("BMW")
car2 = Car("Toyota")
car3 = Car("Benz")
print(Car.get_total()) # Total cars created: 3
نکته آموزشی: صفات کلاس برای همه نمونهها مشترک هستن. با @classmethod میتونیم متدی داشته باشیم که به کلاس خودش دسترسی داره
نه نمونه خاص.
سوال 5: ویژگی محاسباتی با @property
کلاس Circle بسازید که شعاع را بگیرد و مساحت را به عنوان property
محاسبه کند.
# دسترسی به مساحت مثل ویژگی: circle.area
# راه حل:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius ** 2
@property
def circumference(self):
return 2 * 3.14159 * self.radius
circle = Circle(5)
print(f"Area: {circle.area:.2f}") # 78.54
print(f"Circumference: {circle.circumference:.2f}") # 31.42
نکته آموزشی: با @property میتونیم متد را مثل یک
ویژگی استفاده کنیم بدون نیاز به پرانتز. این برای مقادیر محاسباتی مفیده.
سوال 6: اعتبارسنجی با property setter
کلاس Person بسازید که سن را بگیرد و اگر منفی باشد خطا دهد.
# اعتبارسنجی سن هنگام تنظیم
# راه حل:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age # setter صدا زده میشه
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("Age cannot be negative!")
if value > 150:
raise ValueError("Age too high!")
self._age = value
person = Person("علی", 25)
print(person.age) # 25
try:
person.age = -5
except ValueError as e:
print(e) # Age cannot be negative!
نکته آموزشی: با @property و setter میتونیم
کنترل کنیم چه مقادیری قابل تنظیم هستن. این یکی از اصول کپسولهسازیه.
سوال 7: متد استاتیک برای تبدیل واحد
کلاس Temperature بسازید که متد استاتیک برای تبدیل سلسیوس به فارنهایت
داشته باشد.
# تبدیل دما بدون نیاز به ساخت شی
# راه حل:
class Temperature:
@staticmethod
def celsius_to_fahrenheit(celsius):
return (celsius * 9/5) + 32
@staticmethod
def fahrenheit_to_celsius(fahrenheit):
return (fahrenheit - 32) * 5/9
# استفاده بدون ساخت شی
print(Temperature.celsius_to_fahrenheit(25)) # 77.0
print(Temperature.fahrenheit_to_celsius(77)) # 25.0
نکته آموزشی: متدهای استاتیک برای توابع کمکی که به دادههای شی یا کلاس نیاز ندارند استفاده میشن. مثل utility functions.
سوال 8: وراثت ساده
کلاس Animal بسازید که متد sound() داشته
باشد. سپس کلاس Dog که از آن ارثبری کند و متد را بازنویسی کند.
# کلاس فرزند رفتار خاص خودش را دارد
# راه حل:
class Animal:
def __init__(self, name):
self.name = name
def sound(self):
return "Some generic sound"
class Dog(Animal):
def sound(self):
return f"{self.name} says: Woof!"
class Cat(Animal):
def sound(self):
return f"{self.name} says: Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.sound()) # Buddy says: Woof!
print(cat.sound()) # Whiskers says: Meow!
نکته آموزشی: وراثت به ما اجازه میده کد مشترک را در کلاس والد بنویسیم و رفتار اختصاصی را در کلاسهای فرزند پیادهسازی کنیم.
سوال 9: استفاده از super() برای فراخوانی متد والد
کلاس Employee و Manager بسازید که Manager
از Employee ارثبری کند و هم رفتار والد و هم رفتار خودش را داشته باشد.
# استفاده از super() برای ترکیب رفتار والد و فرزند
# راه حل:
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def display_info(self):
print(f"Employee: {self.name}, Salary: {self.salary}")
class Manager(Employee):
def __init__(self, name, salary, department):
super().__init__(name, salary) # فراخوانی سازنده والد
self.department = department
def display_info(self):
super().display_info() # فراخوانی متد والد
print(f"Department: {self.department}")
manager = Manager("سارا", 5000, "IT")
manager.display_info()
# خروجی:
# Employee: سارا, Salary: 5000
# Department: IT
نکته آموزشی: super() به ما اجازه میده متدها و
سازندههای کلاس والد را فراخوانی کنیم و در عین حال رفتار جدید اضافه کنیم.
سوال 10: متد جادویی __add__ برای جمع اشیاء
کلاس Money بسازید که بتوان دو شی از آن را با + جمع کرد.
# money1 + money2
# راه حل:
class Money:
def __init__(self, amount, currency="USD"):
self.amount = amount
self.currency = currency
def __add__(self, other):
if self.currency != other.currency:
raise ValueError("Cannot add different currencies!")
return Money(self.amount + other.amount, self.currency)
def __str__(self):
return f"{self.amount} {self.currency}"
m1 = Money(100, "USD")
m2 = Money(50, "USD")
m3 = m1 + m2
print(m3) # 150 USD
نکته آموزشی: متدهای جادویی مثل __add__ به ما
اجازه میدن رفتار عملگرها را برای کلاس خودمان تعریف کنیم.
سوال 11: متدهای مقایسهای
کلاس Product بسازید که بتوان محصولات را بر اساس قیمت مقایسه کرد.
# استفاده از ==, >, <, >= و <=
# راه حل:
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __eq__(self, other):
return self.price == other.price
def __lt__(self, other):
return self.price < other.price
def __le__(self, other):
return self.price <= other.price
def __gt__(self, other):
return self.price > other.price
def __ge__(self, other):
return self.price >= other.price
p1 = Product("Laptop", 1000)
p2 = Product("Phone", 500)
print(p1 > p2) # True
print(p1 == p2) # False
# مرتبسازی محصولات
products = [p1, p2, Product("Tablet", 750)]
products.sort()
for p in products:
print(f"{p.name}: ${p.price}")
نکته آموزشی: با تعریف متدهای مقایسهای میتونیم اشیاء رو با هم مقایسه کنیم و حتی مرتبسازی کنیم.
سوال 12: کپسولهسازی با ویژگی خصوصی
کلاس BankAccount بسازید که موجودی را خصوصی نگه دارد و فقط از طریق
متدهای deposit و withdraw قابل تغییر باشد.
# محافظت از دادهها با کپسولهسازی
# راه حل:
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.__balance = balance # خصوصی
def deposit(self, amount):
if amount > 0:
self.__balance += amount
print(f"Deposited: {amount}")
else:
print("Amount must be positive!")
def withdraw(self, amount):
if amount > self.__balance:
print("Insufficient funds!")
elif amount <= 0:
print("Amount must be positive!")
else:
self.__balance -= amount
print(f"Withdrawn: {amount}")
def get_balance(self):
return self.__balance
account = BankAccount("علی", 1000)
account.deposit(500)
account.withdraw(200)
print(f"Balance: {account.get_balance()}") # 1300
# دسترسی مستقیم به __balance کار نمیکنه
# print(account.__balance) # AttributeError
نکته آموزشی: ویژگیهای خصوصی با __ شروع میشن و
از بیرون کلاس قابل دسترسی نیستن. این امنیت و کنترل بیشتری ایجاد میکنه.
سوال 13: چندریختی (Polymorphism)
لیستی از حیوانات مختلف بسازید و متد sound() همه را فراخوانی کنید.
# یک رابط واحد، رفتارهای متفاوت
# راه حل:
class Animal:
def sound(self):
pass
class Dog(Animal):
def sound(self):
return "Woof!"
class Cat(Animal):
def sound(self):
return "Meow!"
class Cow(Animal):
def sound(self):
return "Moo!"
class Duck(Animal):
def sound(self):
return "Quack!"
# Polymorphism در عمل
animals = [Dog(), Cat(), Cow(), Duck()]
for animal in animals:
print(animal.sound())
نکته آموزشی: چندریختی یعنی میتونیم یک رابط واحد (متد sound) داشته باشیم اما هر کلاس رفتار خاص خودش را پیادهسازی کنه.
سوال 14: کلاس انتزاعی (Abstract Class)
با استفاده از ABC یک کلاس انتزاعی Payment بسازید و کلاسهای CreditCard و PayPal که از آن ارثبری کنند.
# تعریف رابط مشخص برای انواع پرداخت
# راه حل:
from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def pay(self, amount):
pass
@abstractmethod
def refund(self, amount):
pass
class CreditCard(Payment):
def __init__(self, card_number):
self.card_number = card_number
def pay(self, amount):
print(f"Paid ${amount} using Credit Card {self.card_number}")
def refund(self, amount):
print(f"Refunded ${amount} to Credit Card {self.card_number}")
class PayPal(Payment):
def __init__(self, email):
self.email = email
def pay(self, amount):
print(f"Paid ${amount} using PayPal ({self.email})")
def refund(self, amount):
print(f"Refunded ${amount} to PayPal ({self.email})")
# استفاده
payments = [
CreditCard("1234-5678"),
PayPal("user@example.com")
]
for payment in payments:
payment.pay(100)
payment.refund(20)
نکته آموزشی: کلاسهای انتزاعی یک رابط (interface) تعریف میکنند که همه کلاسهای فرزند باید آن را پیادهسازی کنند. نمیتوان مستقیماً از کلاس انتزاعی شی ساخت.
سوال 15: سیستم مدیریت کتابخانه
سیستمی بسازید با کلاسهای Book و Library
که بتواند کتاب اضافه، حذف و جستوجو کند.
# پروژه کوچک مدیریت کتابخانه
# راه حل:
class Book:
def __init__(self, title, author, isbn):
self.title = title
self.author = author
self.isbn = isbn
def __str__(self):
return f"{self.title} by {self.author} (ISBN: {self.isbn})"
class Library:
def __init__(self):
self.books = []
def add_book(self, book):
self.books.append(book)
print(f"Added: {book.title}")
def remove_book(self, isbn):
for book in self.books:
if book.isbn == isbn:
self.books.remove(book)
print(f"Removed: {book.title}")
return
print("Book not found!")
def search_by_title(self, title):
results = [b for b in self.books if title.lower() in b.title.lower()]
return results
def list_all_books(self):
if not self.books:
print("No books in library")
for book in self.books:
print(book)
# استفاده
library = Library()
library.add_book(Book("1984", "George Orwell", "12345"))
library.add_book(Book("Animal Farm", "George Orwell", "67890"))
library.add_book(Book("To Kill a Mockingbird", "Harper Lee", "11111"))
print("\nAll books:")
library.list_all_books()
print("\nSearch 'Orwell':")
results = library.search_by_title("Orwell")
for book in results:
print(book)
library.remove_book("12345")
نکته آموزشی: این مثال ترکیب چند مفهوم OOP است: کلاسهای مستقل، composition (Library شامل Book ها)، و متدهای کاربردی برای مدیریت کالکشن.
سوال 16: سیستم فروشگاه آنلاین
کلاسهای Product، ShoppingCart و Order بسازید.
# سیستم خرید با سبد خرید و محاسبه کل
# راه حل:
class Product:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
def __str__(self):
return f"{self.name} - ${self.price} x{self.quantity}"
class ShoppingCart:
def __init__(self):
self.items = []
def add_item(self, product):
self.items.append(product)
print(f"Added to cart: {product.name}")
def remove_item(self, product_name):
for item in self.items:
if item.name == product_name:
self.items.remove(item)
print(f"Removed: {product_name}")
return
print("Product not in cart!")
def get_total(self):
return sum(item.price * item.quantity for item in self.items)
def display_cart(self):
if not self.items:
print("Cart is empty")
return
print("Shopping Cart:")
for item in self.items:
print(f" {item}")
print(f"Total: ${self.get_total():.2f}")
class Order:
order_counter = 1000
def __init__(self, cart):
self.order_id = Order.order_counter
Order.order_counter += 1
self.items = cart.items.copy()
self.total = cart.get_total()
self.status = "Pending"
def confirm_order(self):
self.status = "Confirmed"
print(f"Order #{self.order_id} confirmed - Total: ${self.total:.2f}")
# استفاده
cart = ShoppingCart()
cart.add_item(Product("Laptop", 999.99, 1))
cart.add_item(Product("Mouse", 29.99, 2))
cart.add_item(Product("Keyboard", 79.99, 1))
cart.display_cart()
order = Order(cart)
order.confirm_order()
نکته آموزشی: این مثال نشون میده چطور کلاسهای مختلف با هم ارتباط دارند. ShoppingCart محصولات را نگه میداره و Order از آن استفاده میکنه.
سوال 17: سیستم مدیریت کارمندان با سلسله مراتب
کلاسهای Employee، Manager و Developer با محاسبه حقوق متفاوت بسازید.
# وراثت چند سطحی و polymorphism
# راه حل:
class Employee:
def __init__(self, name, base_salary):
self.name = name
self.base_salary = base_salary
def calculate_salary(self):
return self.base_salary
def display_info(self):
print(f"{self.name}: ${self.calculate_salary()}")
class Manager(Employee):
def __init__(self, name, base_salary, bonus):
super().__init__(name, base_salary)
self.bonus = bonus
def calculate_salary(self):
return self.base_salary + self.bonus
def display_info(self):
super().display_info()
print(f" Role: Manager, Bonus: ${self.bonus}")
class Developer(Employee):
def __init__(self, name, base_salary, projects_completed):
super().__init__(name, base_salary)
self.projects_completed = projects_completed
def calculate_salary(self):
project_bonus = self.projects_completed * 100
return self.base_salary + project_bonus
def display_info(self):
super().display_info()
print(f" Role: Developer, Projects: {self.projects_completed}")
class Company:
def __init__(self):
self.employees = []
def hire(self, employee):
self.employees.append(employee)
def payroll(self):
print("=== Company Payroll ===")
total = 0
for emp in self.employees:
emp.display_info()
total += emp.calculate_salary()
print(f"\nTotal Payroll: ${total}")
# استفاده
company = Company()
company.hire(Manager("سارا", 5000, 1500))
company.hire(Developer("علی", 4000, 8))
company.hire(Developer("رضا", 4500, 12))
company.hire(Employee("محمد", 3000))
company.payroll()
نکته آموزشی: این مثال نشون میده چطور یک سیستم سلسلهمراتبی طراحی کنیم که هر نوع کارمند محاسبه حقوق خاص خودش را داره اما همه از رابط مشترک استفاده میکنن.
سوال 18: سیستم رزرو هتل
کلاسهای Room، Guest و Reservation برای مدیریت رزرو هتل بسازید.
# مدیریت اتاقها، مهمانها و رزروها
# راه حل:
from datetime import datetime, timedelta
class Room:
def __init__(self, room_number, room_type, price_per_night):
self.room_number = room_number
self.room_type = room_type
self.price_per_night = price_per_night
self.is_available = True
def __str__(self):
status = "Available" if self.is_available else "Occupied"
return f"Room {self.room_number} ({self.room_type}) - ${self.price_per_night}/night - {status}"
class Guest:
def __init__(self, name, phone, email):
self.name = name
self.phone = phone
self.email = email
def __str__(self):
return f"{self.name} ({self.email})"
class Reservation:
reservation_id = 1000
def __init__(self, guest, room, nights):
self.id = Reservation.reservation_id
Reservation.reservation_id += 1
self.guest = guest
self.room = room
self.nights = nights
self.check_in = datetime.now()
self.check_out = self.check_in + timedelta(days=nights)
self.total_price = room.price_per_night * nights
room.is_available = False
def cancel(self):
self.room.is_available = True
print(f"Reservation #{self.id} cancelled")
def display(self):
print(f"Reservation #{self.id}")
print(f" Guest: {self.guest}")
print(f" Room: {self.room.room_number} ({self.room.room_type})")
print(f" Nights: {self.nights}")
print(f" Total: ${self.total_price}")
# استفاده
guest1 = Guest("علی احمدی", "09123456789", "ali@example.com")
guest2 = Guest("سارا محمدی", "09198765432", "sara@example.com")
room101 = Room(101, "Single", 50)
room201 = Room(201, "Double", 80)
room301 = Room(301, "Suite", 150)
reservation1 = Reservation(guest1, room101, 3)
reservation1.display()
print("\n" + str(room101))
نکته آموزشی: این سیستم composition را نشون میده: Reservation شامل Guest و Room است. همچنین مدیریت state (is_available) و محاسبات خودکار (total_price) را نشان میدهد.
سوال 19: سیستم بانکی کامل با انواع حساب
کلاسهای Account، SavingsAccount و CheckingAccount با قوانین مختلف بسازید.
# سیستم بانکی با چند نوع حساب و قوانین مختلف
# راه حل:
from datetime import datetime
class Account:
account_counter = 10000
def __init__(self, owner, initial_balance=0):
self.account_number = Account.account_counter
Account.account_counter += 1
self.owner = owner
self._balance = initial_balance
self.transactions = []
def deposit(self, amount):
if amount <= 0:
print("Amount must be positive!")
return False
self._balance += amount
self._record_transaction("Deposit", amount)
print(f"Deposited: ${amount}")
return True
def withdraw(self, amount):
if amount <= 0:
print("Amount must be positive!")
return False
if amount > self._balance:
print("Insufficient funds!")
return False
self._balance -= amount
self._record_transaction("Withdraw", amount)
print(f"Withdrawn: ${amount}")
return True
def get_balance(self):
return self._balance
def _record_transaction(self, type, amount):
self.transactions.append({
"type": type,
"amount": amount,
"date": datetime.now(),
"balance": self._balance
})
def print_statement(self):
print(f"\n=== Account Statement for {self.owner} ===")
print(f"Account: {self.account_number}")
print(f"Current Balance: ${self._balance:.2f}\n")
print("Recent Transactions:")
for t in self.transactions[-5:]:
print(f" {t['type']}: ${t['amount']} - Balance: ${t['balance']:.2f}")
class SavingsAccount(Account):
def __init__(self, owner, initial_balance=0, interest_rate=0.03):
super().__init__(owner, initial_balance)
self.interest_rate = interest_rate
self.withdrawal_limit = 3
self.withdrawals_this_month = 0
def withdraw(self, amount):
if self.withdrawals_this_month >= self.withdrawal_limit:
print(f"Withdrawal limit reached ({self.withdrawal_limit}/month)")
return False
if super().withdraw(amount):
self.withdrawals_this_month += 1
return True
return False
def add_interest(self):
interest = self._balance * self.interest_rate
self._balance += interest
self._record_transaction("Interest", interest)
print(f"Interest added: ${interest:.2f}")
class CheckingAccount(Account):
def __init__(self, owner, initial_balance=0, overdraft_limit=500):
super().__init__(owner, initial_balance)
self.overdraft_limit = overdraft_limit
def withdraw(self, amount):
if amount <= 0:
print("Amount must be positive!")
return False
if amount > self._balance + self.overdraft_limit:
print(f"Exceeds overdraft limit (${self.overdraft_limit})")
return False
self._balance -= amount
self._record_transaction("Withdraw", amount)
print(f"Withdrawn: ${amount}")
if self._balance < 0:
print(f"Warning: Overdraft used. Balance: ${self._balance}")
return True
# استفاده
savings = SavingsAccount("علی", 1000, 0.05)
savings.deposit(500)
savings.withdraw(200)
savings.add_interest()
savings.print_statement()
print("\n" + "="*40 + "\n")
checking = CheckingAccount("سارا", 300, 500)
checking.deposit(200)
checking.withdraw(600) # استفاده از overdraft
checking.print_statement()
نکته آموزشی: این مثال پیچیدهترین مثال است و ترکیب کامل OOP را نشان میدهد: وراثت، encapsulation (ویژگی خصوصی _balance)، polymorphism (هر حساب قوانین خودش را دارد) و composition (transactions).
سوال 20: سیستم بلیطفروشی سینما
سیستم کاملی با کلاسهای Movie، Cinema،
Seat و Ticket بسازید.
# سیستم رزرو بلیط سینما با مدیریت صندلیها
# راه حل:
class Movie:
def __init__(self, title, duration, genre):
self.title = title
self.duration = duration
self.genre = genre
def __str__(self):
return f"{self.title} ({self.genre}) - {self.duration} min"
class Seat:
def __init__(self, row, number):
self.row = row
self.number = number
self.is_reserved = False
def reserve(self):
if self.is_reserved:
return False
self.is_reserved = True
return True
def __str__(self):
status = "Reserved" if self.is_reserved else "Available"
return f"{self.row}{self.number} ({status})"
class Ticket:
ticket_counter = 1000
def __init__(self, movie, seat, price):
self.ticket_id = Ticket.ticket_counter
Ticket.ticket_counter += 1
self.movie = movie
self.seat = seat
self.price = price
def display(self):
print(f"\n{'='*40}")
print(f" TICKET #{self.ticket_id}")
print(f"{'='*40}")
print(f" Movie: {self.movie.title}")
print(f" Seat: {self.seat.row}{self.seat.number}")
print(f" Price: ${self.price}")
print(f"{'='*40}\n")
class Cinema:
def __init__(self, name):
self.name = name
self.movies = []
self.seats = self._create_seats()
def _create_seats(self):
seats = []
for row in 'ABCDEFGH':
for num in range(1, 11):
seats.append(Seat(row, num))
return seats
def add_movie(self, movie):
self.movies.append(movie)
print(f"Added movie: {movie.title}")
def show_movies(self):
print(f"\n=== Movies at {self.name} ===")
for i, movie in enumerate(self.movies, 1):
print(f"{i}. {movie}")
def show_available_seats(self):
print("\n=== Available Seats ===")
for i, seat in enumerate(self.seats):
if not seat.is_reserved:
print(seat, end=" ")
if (i + 1) % 10 == 0:
print()
def book_ticket(self, movie_index, seat_row, seat_number, price=10):
if movie_index < 0 or movie_index >= len(self.movies):
print("Invalid movie!")
return None
seat = self._find_seat(seat_row, seat_number)
if not seat:
print("Seat not found!")
return None
if seat.reserve():
ticket = Ticket(self.movies[movie_index], seat, price)
print("Booking successful!")
return ticket
else:
print("Seat already reserved!")
return None
def _find_seat(self, row, number):
for seat in self.seats:
if seat.row == row and seat.number == number:
return seat
return None
# استفاده
cinema = Cinema("Galaxy Cinema")
# اضافه کردن فیلمها
cinema.add_movie(Movie("Inception", 148, "Sci-Fi"))
cinema.add_movie(Movie("The Dark Knight", 152, "Action"))
cinema.add_movie(Movie("Interstellar", 169, "Sci-Fi"))
# نمایش فیلمها
cinema.show_movies()
# رزرو بلیط
ticket1 = cinema.book_ticket(0, 'A', 5, 12)
if ticket1:
ticket1.display()
ticket2 = cinema.book_ticket(1, 'B', 8, 15)
if ticket2:
ticket2.display()
# تلاش برای رزرو دوباره همان صندلی
ticket3 = cinema.book_ticket(0, 'A', 5, 12)
# نمایش صندلیهای موجود (فقط چند تا اول)
print("\nFirst 20 seats status:")
for seat in cinema.seats[:20]:
print(seat)
نکته آموزشی: این پروژه کامل نشان میدهد چطور یک سیستم واقعی طراحی میشود با چندین کلاس که با هم تعامل دارند. شامل composition، encapsulation، state management و business logic است. این نوع پروژه برای portfolio عالی است.
دیدگاهتان را بنویسید