آموزش پایتون - مفاهیم شیءگرایی
OOP مخفف عبارت برنامهنویسی شیءگرا (Object-oriented programming) است. این اصطلاح به الگوی برنامهنویسیای اشاره دارد که از مفهوم اشیا استفاده میکند؛ اشیایی که نمایانگر موجودیتهای دنیای واقعی با وضعیت و رفتار مشخص هستند. این فصل به شما کمک میکند تا در استفاده از قابلیتهای برنامهنویسی شیءگرا در زبان پایتون مهارت پیدا کنید.
پایتون یک زبان برنامهنویسی است که از برنامهنویسی شیءگرا پشتیبانی میکند. این ویژگی باعث میشود ایجاد و استفاده از کلاسها و اشیا بهسادگی انجام شود. اگر قبلاً تجربهای در زمینه برنامهنویسی شیءگرا نداشتهاید، در جای مناسبی هستید. بیایید با معرفی کوتاهی از برنامهنویسی شیءگرا (OOP) شروع کنیم تا با این مفهوم آشنا شوید.
رویکرد برنامهنویسی رویهای
زبانهای برنامهنویسی اولیه که در دهههای ۱۹۵۰ و ۱۹۶۰ توسعه یافتند، بهعنوان زبانهای رویهای (یا مبتنی بر رویه) شناخته میشوند.
یک برنامه کامپیوتری، روش انجام یک کار مشخص را با نوشتن مجموعهای از دستورالعملها به ترتیب منطقی توصیف میکند. منطق یک برنامه پیچیدهتر به بخشهای کوچکتر، مستقل و قابل استفاده مجدد تقسیم میشود که از مجموعهای از دستورها تشکیل شدهاند و تابع نام دارند.
هر تابع بهگونهای نوشته میشود که بتواند با سایر توابع برنامه تعامل داشته باشد. دادههای متعلق به یک تابع را میتوان بهراحتی در قالب آرگومانها با توابع دیگر به اشتراک گذاشت. همچنین، تابع فراخوانیشده میتواند نتیجه خود را به تابع فراخواننده بازگرداند.
مهمترین مشکلات مرتبط با رویکرد رویهای عبارتاند از:
-
رویکرد بالا به پایین آن، نگهداری و بهروزرسانی برنامه را دشوار میکند.
-
این رویکرد از تعداد زیادی متغیر و داده سراسری استفاده میکند که مطلوب نیست. وجود تعداد بیش از حد دادههای سراسری، سربار مصرف حافظه را افزایش میدهد.
-
در این رویکرد، اهمیت بیشتری به فرایندها داده میشود و دادهها از اهمیت یکسانی برخوردار نیستند. در نتیجه، دادهها بدیهی فرض میشوند و آزادانه در سراسر برنامه جابهجا میشوند.
-
جابهجایی دادهها بین توابع هیچ محدودیتی ندارد. این موضوع با سناریوهای دنیای واقعی سازگار نیست؛ زیرا در دنیای واقعی، معمولاً ارتباط مشخصی بین یک تابع و دادههایی وجود دارد که انتظار میرود آن تابع پردازش کند.
پایتون - مفاهیم برنامهنویسی شیءگرا
در دنیای واقعی، با اشیایی مانند دانشجو، کارمند، فاکتور، خودرو و غیره سروکار داریم و آنها را پردازش میکنیم. اشیا نه صرفاً داده هستند و نه فقط تابع، بلکه ترکیبی از هر دو به شمار میروند. هر شیء در دنیای واقعی دارای ویژگیها و رفتارهای مرتبط با خود است.

ویژگیها
-
نام، کلاس، درسها، نمرهها و غیره مربوط به دانشجو
-
نام، عنوان شغلی، بخش، حقوق و غیره مربوط به کارمند
-
شماره فاکتور، مشتری، کد و نام محصول، قیمت و تعداد و غیره در یک فاکتور
-
شماره ثبت، مالک، شرکت سازنده، برند، قدرت موتور، سرعت و غیره مربوط به خودرو
هر ویژگی دارای یک مقدار مشخص است. ویژگی معادل داده است.
رفتار
پردازش ویژگیهای مرتبط با یک شیء را رفتار آن شیء مینامند.
-
محاسبه درصد نمرههای دانشجو
-
محاسبه پاداشهای قابل پرداخت به کارمند
-
اعمال مالیات کالا و خدمات (GST) بر مبلغ فاکتور
-
اندازهگیری سرعت خودرو
رفتار معادل تابع است. در زندگی واقعی، ویژگیها و رفتارها مستقل از یکدیگر نیستند، بلکه در کنار هم وجود دارند.
مهمترین ویژگی رویکرد شیءگرا، تعریف ویژگیها و عملکردهای مربوط به آنها در قالب یک واحد مستقل به نام کلاس است. کلاس مانند یک الگو عمل میکند که میتوان از آن برای ایجاد تمام اشیایی با ویژگیها و رفتارهای مشابه استفاده کرد.
در برنامهنویسی شیءگرا، کلاس مشخص میکند که اشیای آن چه ویژگیهایی دارند و چگونه رفتار میکنند. از سوی دیگر، شیء نمونهای از یک کلاس است.
اصول مفاهیم برنامهنویسی شیءگرا
الگوی برنامهنویسی شیءگرا با اصول زیر شناخته میشود:
-
کلاس (Class)
-
شیء (Object)
-
کپسولهسازی (Encapsulation)
-
وراثت (Inheritance)
-
چندریختی (Polymorphism)

کلاس و شیء
کلاس الگویی است که توسط کاربر تعریف میشود و مشخص میکند اشیای متعلق به آن کلاس چه مجموعهای از ویژگیها را دارند. این ویژگیها شامل اعضای دادهای (متغیرهای کلاس و متغیرهای نمونه) و متدها هستند که از طریق نماد نقطه به آنها دسترسی پیدا میکنیم.
شیء به نمونهای از یک کلاس مشخص اشاره دارد. برای مثال، شیئی با نام obj که به کلاس Circle تعلق دارد، نمونهای از آن کلاس است. هر شیء نمونهای منحصربهفرد از یک ساختار داده است که کلاس آن را تعریف میکند. یک شیء هم شامل اعضای دادهای (متغیرهای کلاس و متغیرهای نمونه) و هم شامل متدهاست.
مثال
مثال زیر نحوه ایجاد یک کلاس و شیء مربوط به آن را در پایتون نشان میدهد.
# defining class
class Smartphone:
# constructor
def __init__(self, device, brand):
self.device = device
self.brand = brand
# method of the class
def description(self):
return f"{self.device} of {self.brand} supports Android 14"
# creating object of the class
phoneObj = Smartphone("Smartphone", "Samsung")
print(phoneObj.description())
با اجرای کد بالا، خروجی زیر نمایش داده میشود:
Smartphone of Samsung supports Android 14
کپسولهسازی
اعضای دادهای یک کلاس برای پردازش فقط در دسترس توابعی هستند که درون همان کلاس تعریف شدهاند. از سوی دیگر، توابع کلاس از خارج از محدوده کلاس نیز قابل دسترسی هستند. بنابراین، دادههای شیء از محیط بیرونی کلاس پنهان میمانند. تابع کلاس که متد نیز نامیده میشود، دادههای شیء را کپسوله میکند تا از دسترسی غیرمجاز به آنها جلوگیری شود.
مثال
در این مثال، از مفهوم کپسولهسازی برای تعیین قیمت یک رایانه رومیزی استفاده میکنیم.
class Desktop:
def __init__(self):
self.__max_price = 25000
def sell(self):
return f"Selling Price: {self.__max_price}"
def set_max_price(self, price):
if price > self.__max_price:
self.__max_price = price
# Object
desktopObj = Desktop()
print(desktopObj.sell())
# modifying the price directly
desktopObj.__max_price = 35000
print(desktopObj.sell())
# modifying the price using setter function
desktopObj.set_max_price(35000)
print(desktopObj.sell())
با اجرای کد بالا، نتیجه زیر تولید میشود:
Selling Price: 25000
Selling Price: 25000
Selling Price: 35000
وراثت
رویکرد مدلسازی نرمافزار در برنامهنویسی شیءگرا امکان گسترش قابلیتهای یک کلاس موجود و ایجاد کلاس جدید را فراهم میکند؛ بنابراین، لازم نیست کلاس جدید را از ابتدا بسازیم. در اصطلاح برنامهنویسی شیءگرا، کلاس موجود «کلاس پایه یا والد» (base or parent class) نامیده میشود و کلاس جدید «کلاس فرزند یا زیرکلاس» (child or sub class) نام دارد.
کلاس فرزند، تعریف دادهها و متدهای کلاس والد را به ارث میبرد. این قابلیت استفاده مجدد از ویژگیهای موجود را آسان میکند. کلاس فرزند میتواند چند تعریف جدید اضافه کند یا یکی از توابع کلاس پایه را بازتعریف کند.
نحو
کلاسهای مشتقشده تقریباً مانند کلاس والد خود تعریف میشوند؛ با این تفاوت که فهرستی از کلاسهای پایهای که قرار است از آنها ارثبری شود، پس از نام کلاس قرار میگیرد:
class SubClassName (ParentClass1[, ParentClass2, ...]):
'Optional class documentation string'
class_suite
مثال
مثال زیر مفهوم وراثت در پایتون را نشان میدهد:
#!/usr/bin/python
# define parent class
class Parent:
parentAttr = 100
def __init__(self):
print ("Calling parent constructor")
def parentMethod(self):
print ("Calling parent method")
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print ("Parent attribute :", Parent.parentAttr)
# define child class
class Child(Parent):
def __init__(self):
print ("Calling child constructor")
def childMethod(self):
print ("Calling child method")
# instance of child
c = Child()
# child calls its method
c.childMethod()
# calls parent's method
c.parentMethod()
# again call parent's method
c.setAttr(200)
# again call parent's method
c.getAttr()
با اجرای کد بالا، نتیجه زیر تولید میشود:
Calling child constructor
Calling child method
Calling parent method
Parent attribute : 200
به همین روش، میتوانید یک کلاس را از چند کلاس والد بهصورت زیر مشتق کنید:
class A: # define your class A
.....
class B: # define your class B
.....
class C(A, B): # subclass of A and B
.....
برای بررسی رابطه بین دو کلاس و نمونههای آنها میتوانید از توابع issubclass() یا isinstance() استفاده کنید.
-
تابع بولی issubclass(sub, sup) در صورتی مقدار true را برمیگرداند که زیرکلاس دادهشده با نام sub واقعاً زیرکلاسی از کلاس والد با نام sup باشد.
-
تابع بولی isinstance(obj, Class) در صورتی مقدار true را برمیگرداند که obj نمونهای از کلاس Class یا نمونهای از یکی از زیرکلاسهای آن باشد.
چندریختی
چندریختی واژهای یونانی به معنای داشتن شکلهای گوناگون است. در برنامهنویسی شیءگرا، چندریختی زمانی رخ میدهد که هر زیرکلاس، پیادهسازی مخصوص به خود را برای یک متد انتزاعی تعریفشده در کلاس پایه ارائه کند.
همیشه میتوانید متدهای کلاس والد را بازنویسی کنید. یکی از دلایل بازنویسی متدهای کلاس والد این است که ممکن است بخواهید عملکردی ویژه یا متفاوت برای زیرکلاس خود داشته باشید.
مثال
در این مثال، متد کلاس والد را بازنویسی میکنیم.
# define parent class
class Parent:
def myMethod(self):
print ("Calling parent method")
# define child class
class Child(Parent):
def myMethod(self):
print ("Calling child method")
# instance of child
c = Child()
# child calls overridden method
c.myMethod()
با اجرای کد بالا، نتیجه زیر تولید میشود:
Calling child method
بازنویسی و سربارگذاری متدهای پایه در پایتون
جدول زیر برخی از عملکردهای عمومی را فهرست میکند که میتوانید آنها را در کلاسهای خود بازنویسی کنید:
| شماره | متد، توضیحات و نمونه فراخوانی |
|---|---|
| 1 |
__init__ ( self [,args...] ) سازنده (با هر تعداد آرگومان اختیاری) نمونه فراخوانی: obj = className(args) |
| 2 |
__del__( self ) مخرب؛ برای حذف یک شیء نمونه فراخوانی: del obj |
| 3 |
__repr__( self ) نمایش رشتهای که امکان ارزیابی مجدد آن وجود دارد نمونه فراخوانی: repr(obj) |
| 4 |
__str__( self ) نمایش رشتهای قابل چاپ نمونه فراخوانی: str(obj) |
| 5 |
__cmp__ ( self, x ) مقایسه اشیا نمونه فراخوانی: cmp(obj, x) |
سربارگذاری عملگرها در پایتون
فرض کنید کلاسی به نام Vector برای نمایش بردارهای دوبعدی ایجاد کردهاید. وقتی از عملگر جمع برای جمع کردن این بردارها استفاده میکنید، چه اتفاقی میافتد؟ به احتمال زیاد پایتون خطا میدهد.
با این حال، میتوانید متد __add__ را در کلاس خود تعریف کنید تا جمع بردارها را انجام دهد. در این صورت، عملگر جمع همانطور که انتظار دارید عمل خواهد کرد:
مثال
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
def __add__(self,other):
return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)
با اجرای کد بالا، نتیجه زیر تولید میشود:
Vector(7,8)
پیشرفت مطالعه: وضعیت خواندهشدن این درس فقط در همین مرورگر ذخیره میشود؛ بدون نیاز به حساب کاربری.