آموزش پایتون, پایتون سطح متوسط

مبانی برنامه‌نویسی شی‌گرا در پایتون؛ آموزش Class و Object

مبانی برنامه‌نویسی شی‌گرا در پایتون

🗓 آخرین به‌روزرسانی:

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

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

برنامه‌نویسی شی‌گرا چیست؟

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

برای نمونه، در یک فروشگاه اینترنتی می‌توانیم موجودیت‌های زیر را به‌صورت شیء مدل کنیم:

  • محصول
  • مشتری
  • سبد خرید
  • سفارش
  • پرداخت

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

چرا از برنامه‌نویسی شی‌گرا استفاده می‌کنیم؟

  • مدل‌سازی بهتر: مفاهیم واقعی مانند کاربر، محصول یا خودرو را مستقیم در کد نمایش می‌دهیم.
  • سازمان‌دهی کد: داده‌های مرتبط در یک ساختار مشخص قرار می‌گیرند.
  • استفاده مجدد: از یک کلاس می‌توان تعداد زیادی شیء ساخت.
  • نگهداری ساده‌تر: تغییرات مربوط به یک مفهوم در بخش مشخصی از برنامه انجام می‌شود.
  • توسعه‌پذیری: افزودن قابلیت‌های جدید به پروژه‌های بزرگ کنترل‌پذیرتر می‌شود.
  • همکاری تیمی: مسئولیت بخش‌های مختلف برنامه واضح‌تر است.

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

کلاس در پایتون چیست؟

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

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

ساده‌ترین ساختار تعریف کلاس در پایتون:

class Product:
    pass

کلمه کلیدی class برای تعریف کلاس استفاده می‌شود. نام کلاس در این مثال Product است. دستور pass نیز به این معنی است که بدنه کلاس فعلاً خالی است.

قواعد نام‌گذاری کلاس‌ها

براساس قرارداد رایج پایتون، نام کلاس‌ها با سبک PascalCase نوشته می‌شود؛ یعنی ابتدای هر واژه با حرف بزرگ آغاز می‌شود:

class Product:
    pass


class ShoppingCart:
    pass


class BankAccount:
    pass

نمونه‌های نام‌گذاری نامناسب:

class product:
    pass


class shopping_cart:
    pass

این نام‌ها از نظر دستوری ممکن است کار کنند، اما با قرارداد استاندارد خوانایی کد پایتون هماهنگ نیستند.

Object یا شیء چیست؟

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

class Product:
    pass


product_1 = Product()
product_2 = Product()

print(product_1)
print(product_2)

با نوشتن نام کلاس و پرانتز، یک نمونه جدید ساخته می‌شود:

product_1 = Product()

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

تفاوت Class و Object

مفهومتعریفنمونه
Classالگو یا نوعی که ساختار کلی را تعریف می‌کندProduct
Objectنمونه واقعی ساخته‌شده از کلاسproduct_1
Instantiationفرایند ساخت شیء از کلاسProduct()
Instanceنام دیگر شیء ساخته‌شده از یک کلاسproduct_2

یک کلاس می‌تواند مبنای ساخت تعداد زیادی شیء باشد:

class Customer:
    pass


customer_1 = Customer()
customer_2 = Customer()
customer_3 = Customer()

ویژگی یا Attribute چیست؟

ویژگی یا Attribute اطلاعاتی است که به یک کلاس یا شیء متصل می‌شود. برای دسترسی به ویژگی از نقطه استفاده می‌کنیم:

object_name.attribute_name

در این مرحله، ویژگی‌ها را پس از ساخت شیء به آن اضافه می‌کنیم:

class Product:
    pass


product_1 = Product()
product_1.name = "Keyboard"
product_1.price = 1200000
product_1.stock = 8

print(product_1.name)
print(product_1.price)
print(product_1.stock)

ویژگی‌های name، price و stock فقط به شیء product_1 تعلق دارند.

هر شیء وضعیت مستقل دارد

دو شیء از یک کلاس می‌توانند مقادیر متفاوتی داشته باشند:

class Product:
    pass


product_1 = Product()
product_1.name = "Keyboard"
product_1.price = 1200000

product_2 = Product()
product_2.name = "Mouse"
product_2.price = 650000

print(product_1.name, product_1.price)
print(product_2.name, product_2.price)

تغییر قیمت یک شیء، قیمت شیء دیگر را تغییر نمی‌دهد:

product_1.price = 1350000

print(product_1.price)
print(product_2.price)

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

ویژگی کلاسی چیست؟

ویژگی کلاسی یا Class Attribute داخل بدنه کلاس تعریف می‌شود و میان همه نمونه‌ها مشترک است:

class Product:
    currency = "Toman"


product_1 = Product()
product_2 = Product()

print(product_1.currency)
print(product_2.currency)
print(Product.currency)

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

تفاوت ویژگی کلاسی و ویژگی نمونه

نوع ویژگیمحل تعریفکاربرد
Class Attributeداخل بدنه کلاساطلاعات مشترک میان نمونه‌ها
Instance Attributeروی هر شیءاطلاعات اختصاصی هر نمونه
class Product:
    store_name = "Dev Source Store"


product_1 = Product()
product_1.name = "Python Course"
product_1.price = 900000

product_2 = Product()
product_2.name = "Django Course"
product_2.price = 1400000

print(product_1.store_name)
print(product_1.name)

print(product_2.store_name)
print(product_2.name)

store_name مشترک است؛ اما name و price برای هر نمونه مقدار جداگانه دارند.

بازنویسی ویژگی کلاسی در یک نمونه

اگر ویژگی هم‌نام را روی یک شیء مقداردهی کنیم، آن شیء مقدار اختصاصی خود را خواهد داشت:

class Product:
    currency = "Toman"


product_1 = Product()
product_2 = Product()

product_1.currency = "Dollar"

print(product_1.currency)
print(product_2.currency)
print(Product.currency)

خروجی:

Dollar
Toman
Toman

مقدار ویژگی کلاسی تغییر نکرده است؛ فقط شیء اول ویژگی نمونه‌ای هم‌نام دریافت کرده است.

تغییر ویژگی کلاسی

برای تغییر مقدار مشترک بهتر است آن را از طریق خود کلاس تغییر دهیم:

class Product:
    currency = "Toman"


product_1 = Product()
product_2 = Product()

Product.currency = "Rial"

print(product_1.currency)
print(product_2.currency)

تا زمانی که یک شیء مقدار اختصاصی هم‌نام نداشته باشد، مقدار جدید کلاس را مشاهده می‌کند.

خطر ویژگی‌های کلاسی تغییرپذیر

استفاده از لیست یا دیکشنری به‌عنوان ویژگی کلاسی می‌تواند باعث اشتراک ناخواسته داده میان همه اشیاء شود:

class Cart:
    items = []


cart_1 = Cart()
cart_2 = Cart()

cart_1.items.append("Keyboard")

print(cart_1.items)
print(cart_2.items)

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

بررسی نوع شیء با type

class Product:
    pass


product = Product()

print(type(product))
print(type(Product))

شیء product از نوع Product است. خود کلاس نیز در پایتون یک شیء محسوب می‌شود و نوع آن معمولاً type است.

بررسی نمونه با isinstance

تابع isinstance() بررسی می‌کند یک شیء نمونه‌ای از یک کلاس مشخص هست یا خیر:

class Product:
    pass


product = Product()

print(isinstance(product, Product))
print(isinstance(product, str))

خروجی:

True
False

این تابع در اعتبارسنجی ورودی‌ها و بررسی نوع اشیاء کاربرد دارد.

بررسی ویژگی با hasattr

برای بررسی وجود یک ویژگی می‌توان از hasattr() استفاده کرد:

class Product:
    pass


product = Product()
product.name = "Monitor"

print(hasattr(product, "name"))
print(hasattr(product, "price"))

خواندن و تنظیم پویا با getattr و setattr

در شرایطی که نام ویژگی به‌صورت رشته دریافت می‌شود، توابع زیر مفید هستند:

class Product:
    pass


product = Product()

setattr(product, "name", "Headphone")
setattr(product, "price", 1800000)

print(getattr(product, "name"))
print(getattr(product, "price"))
print(getattr(product, "stock", 0))

آرگومان سوم getattr() مقدار پیش‌فرض است و در صورت نبود ویژگی برگردانده می‌شود.

حذف ویژگی با delattr

class Product:
    pass


product = Product()
product.name = "Webcam"

print(hasattr(product, "name"))

delattr(product, "name")

print(hasattr(product, "name"))

حذف ویژگی باید با دقت انجام شود؛ زیرا دسترسی بعدی به ویژگی حذف‌شده می‌تواند خطای AttributeError ایجاد کند.

هویت اشیاء و تابع id

هر شیء در زمان اجرا هویت مستقل دارد. تابع id() یک شناسه عددی برای هویت شیء برمی‌گرداند:

class Product:
    pass


product_1 = Product()
product_2 = Product()

print(id(product_1))
print(id(product_2))
print(product_1 is product_2)

حتی اگر ویژگی‌های دو شیء یکسان باشند، آن‌ها می‌توانند دو شیء مستقل باشند:

product_1.name = "Mouse"
product_2.name = "Mouse"

print(product_1.name == product_2.name)
print(product_1 is product_2)

عملگر == در اینجا مقدار ویژگی‌ها را مقایسه می‌کند، اما is هویت دو مرجع را بررسی می‌کند.

چند نام برای یک شیء

ممکن است دو متغیر به یک شیء واحد اشاره کنند:

class Product:
    pass


product_1 = Product()
product_1.name = "Laptop"

product_2 = product_1
product_2.name = "Gaming Laptop"

print(product_1.name)
print(product_1 is product_2)

چون هر دو متغیر به یک شیء اشاره می‌کنند، تغییر از طریق یکی در دیگری نیز دیده می‌شود. این رفتار Aliasing نام دارد.

کلاس در برابر دیکشنری

برای نگهداری داده‌های ساده می‌توان از دیکشنری استفاده کرد:

product = {
    "name": "Keyboard",
    "price": 1200000,
    "stock": 8
}

print(product["name"])

نسخه مبتنی بر کلاس:

class Product:
    pass


product = Product()
product.name = "Keyboard"
product.price = 1200000
product.stock = 8

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

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

نگهداری اشیاء در لیست

اشیاء را می‌توان مانند انواع دیگر پایتون در لیست ذخیره کرد:

class Product:
    pass


product_1 = Product()
product_1.name = "Keyboard"
product_1.price = 1200000

product_2 = Product()
product_2.name = "Mouse"
product_2.price = 650000

products = [product_1, product_2]

for product in products:
    print(product.name, product.price)

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

مستندسازی کلاس با Docstring

می‌توان هدف کلاس را با Docstring توضیح داد:

class Product:
    """Represent a product in the store."""

    currency = "Toman"

برای مشاهده مستندات:

print(Product.__doc__)
help(Product)

Docstring برای پروژه‌های تیمی و تولید مستندات خودکار بسیار مفید است.

پروژه عملی: فهرست محصولات فروشگاه

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

class Product:
    store_name = "Dev Source Store"
    currency = "Toman"


product_1 = Product()
product_1.code = "P-101"
product_1.name = "Python Course"
product_1.price = 900000
product_1.stock = 12

product_2 = Product()
product_2.code = "P-102"
product_2.name = "Django Course"
product_2.price = 1400000
product_2.stock = 0

product_3 = Product()
product_3.code = "P-103"
product_3.name = "FastAPI Course"
product_3.price = 1100000
product_3.stock = 5

products = [product_1, product_2, product_3]

total_stock = 0

print(Product.store_name)
print("-" * 40)

for product in products:
    total_stock += product.stock

    if product.stock > 0:
        status = "Available"
    else:
        status = "Out of stock"

    print("Code:", product.code)
    print("Name:", product.name)
    print("Price:", product.price, product.currency)
    print("Status:", status)
    print("-" * 40)

print("Total stock:", total_stock)

در این پروژه:

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

محدودیت روش فعلی ساخت شیء

در مثال‌های این درس، ویژگی‌ها را پس از ساخت شیء به‌صورت دستی اضافه کردیم:

product = Product()
product.name = "Keyboard"
product.price = 1200000

این روش چند مشکل دارد:

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

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

تمرین‌های درس

  1. کلاسی با نام Book بسازید و سه شیء با ویژگی‌های عنوان، نویسنده و قیمت ایجاد کنید.
  2. کلاسی با نام Student بسازید و ویژگی کلاسی school_name را برای همه دانش‌آموزان تعریف کنید.
  3. پنج شیء محصول بسازید، آن‌ها را در لیست قرار دهید و فقط محصولات موجود را چاپ کنید.
  4. با isinstance() بررسی کنید یک مقدار نمونه کلاس موردنظر هست یا خیر.
  5. با getattr() ویژگی‌ای را بخوانید که در صورت نبود مقدار پیش‌فرض داشته باشد.
  6. دو متغیر را به یک شیء متصل کنید و اثر تغییر ویژگی از طریق هر مرجع را بررسی کنید.

اشتباهات رایج

  • نوشتن نام کلاس با سبک نام‌گذاری متغیرها
  • اشتباه‌گرفتن کلاس با شیء ساخته‌شده از آن
  • استفاده از ویژگی کلاسی تغییرپذیر برای داده اختصاصی نمونه‌ها
  • فرض‌کردن اینکه دو شیء با مقادیر مشابه، یک هویت دارند
  • استفاده از is به‌جای == برای مقایسه معمول مقادیر
  • افزودن دستی ویژگی‌ها بدون ساختار ثابت در پروژه بزرگ
  • استفاده از کلاس برای مسئله‌ای که با یک تابع یا دیکشنری ساده حل می‌شود

جمع‌بندی

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

همچنین تفاوت ویژگی کلاسی و ویژگی نمونه، هویت اشیاء، چند مرجع برای یک شیء، توابع type، isinstance، hasattr، getattr و setattr و نگهداری اشیاء در لیست را بررسی کردیم.

در درس بعدی، سازنده __init__، پارامتر self، متدهای نمونه و مفهوم وراثت را یاد می‌گیریم تا کلاس‌های واقعی‌تر و قابل استفاده‌تری طراحی کنیم.

ادامه مسیر یادگیری پایتون

برای یادگیری منظم، آموزش‌های مرتبط زیر را به‌ترتیب دنبال کنید:

📌 همچنین بخوانید: بازار کار پایتون در ایران، دستورهای break، continue و pass در پایتون و ساخت ربات تلگرام با پایتون

می‌خواهی پیشرفتت ذخیره شود؟با ورود یا ثبت‌نام رایگان، درس‌های تمام‌شده را علامت بزن و درصد پیشرفتت را در حساب کاربری ببین.ورود / ثبت‌نام برای ذخیره پیشرفت
درس بعدی · دوره پایتون متوسط (17 از 18)سازنده __init__، متدها و وراثت در پایتون ←
آموزش‌های تازه، کد آماده و رفع خطاهر هفته در کانال Dev Source؛ رایگان.
🧭 مسیر یادگیری پایتون از صفر
ماژول 9: برنامه‌نویسی شیءگرا — درس 39 از 44

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

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *