مبانی برنامهنویسی شیگرا در پایتون؛ آموزش 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__ ویژگیهای ضروری را هنگام ساخت شیء دریافت میکنیم و با متدها رفتارهای مرتبط را داخل کلاس قرار میدهیم.
تمرینهای درس
- کلاسی با نام
Bookبسازید و سه شیء با ویژگیهای عنوان، نویسنده و قیمت ایجاد کنید. - کلاسی با نام
Studentبسازید و ویژگی کلاسیschool_nameرا برای همه دانشآموزان تعریف کنید. - پنج شیء محصول بسازید، آنها را در لیست قرار دهید و فقط محصولات موجود را چاپ کنید.
- با
isinstance()بررسی کنید یک مقدار نمونه کلاس موردنظر هست یا خیر. - با
getattr()ویژگیای را بخوانید که در صورت نبود مقدار پیشفرض داشته باشد. - دو متغیر را به یک شیء متصل کنید و اثر تغییر ویژگی از طریق هر مرجع را بررسی کنید.
اشتباهات رایج
- نوشتن نام کلاس با سبک نامگذاری متغیرها
- اشتباهگرفتن کلاس با شیء ساختهشده از آن
- استفاده از ویژگی کلاسی تغییرپذیر برای داده اختصاصی نمونهها
- فرضکردن اینکه دو شیء با مقادیر مشابه، یک هویت دارند
- استفاده از
isبهجای==برای مقایسه معمول مقادیر - افزودن دستی ویژگیها بدون ساختار ثابت در پروژه بزرگ
- استفاده از کلاس برای مسئلهای که با یک تابع یا دیکشنری ساده حل میشود
جمعبندی
در این درس با مبانی برنامهنویسی شیگرا در پایتون آشنا شدیم. یاد گرفتیم کلاس الگویی برای ساخت اشیاء است، هر شیء یک نمونه مستقل محسوب میشود و اطلاعات آن در ویژگیها نگهداری میشود.
همچنین تفاوت ویژگی کلاسی و ویژگی نمونه، هویت اشیاء، چند مرجع برای یک شیء، توابع type، isinstance، hasattr، getattr و setattr و نگهداری اشیاء در لیست را بررسی کردیم.
در درس بعدی، سازنده __init__، پارامتر self، متدهای نمونه و مفهوم وراثت را یاد میگیریم تا کلاسهای واقعیتر و قابل استفادهتری طراحی کنیم.
ادامه مسیر یادگیری پایتون
برای یادگیری منظم، آموزشهای مرتبط زیر را بهترتیب دنبال کنید:
📌 همچنین بخوانید: بازار کار پایتون در ایران، دستورهای break، continue و pass در پایتون و ساخت ربات تلگرام با پایتون
برای ذخیره پیشرفت در همه دستگاهها و دریافت گواهی پایان مسیر، رایگان ثبتنام کن یا وارد شو.