آموزش پایتون - برنامه‌نویسی شی‌گرا

در این سری از مقالات قصد دارم نکات کلیدی که در زبان پایتون وجود دارد را ارائه کنم. در این مجموعه تلاش شده اطلاعات مختصر و کوتاه باشند و بر مهمترین نکات مورد نیاز تمرکز شود. 

بعد از گذراندن این دوره شما قادر خواهید بود که از هر کتابخانه پایتون استفاده کنید و بسته‌های پایتونی که خودتان نوشته‌اید را اجرا کنید.

عناوین دوره:

  1. مقدمه
  2. نصب و راه‌اندازی
  3. متغیرها و انواع داده
  4. عبارت‌های کنترلی
  5. توابع و ماژول‌ها
  6. هویت اشیاء
  7. مدیریت خطا
  8. برنامه‌نویسی شی‌گرا
  9. بسته‌بندی و انتشار کد

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

مفهوم برنامه نویسی شی‌گرا OOP بر استفاده مجدد کد اشاره دارد. این مفهوم با نام DRY (Don't Repeat Yourself) نیز شناخته می‌شود. در ادامه اصول اساسی مفهوم برنامه‌نویسی شی‌گرا در پایتون را بررسی می‌کنیم:

کلاس

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

کلاس با استفاده از کلمه کلیدی class تعریف می‌شود. در زیر یک نمونه کلاس تعریف شده است:

class Person:
    "This is a person class"
    age = 10

    def greet(self):
        print('Hello')

به محض تعریف یک کلاس، یک شی از کلاس با همان نام ایجاد می‌شود که به امکان دسترسی به خاصیت‌های مختلف کلاس را می‌دهد:

Image for post
Image for post

همانطور که می‌بینید با شی کلاس Person به خاصیت‌های کلاس می‌توان دسترسی پیدا کرد. خاصیت‌های یک کلاس همانطور که در مثال فوق مشاهده می‌کنید یا از نوع دیتا هستند و یا تابع.

توجه داشته باشید که اولین کامنتی که در ابتدای تعریف کلاس وارد می‌کنید بعنوان توضیحات کلاس (docstring) در نظر گرفته می‌شود اگرچه این مورد اختیاری است اما توصیه می‌شود برای کلاس‌های خود از آن استفاده کنید. 

در کلاس پایتون متدهای ویژه‌ای تعریف شده‌اند، یکی از آنها __doc__ (با دو زیر خط) است که می‌توان با کمک آن توضیحات کلاس را ببینیم.

ایجاد شی از کلاس

برای ایجاد نمونه اشیاء جدید از یک کلاس تنها کافی است نام کلاس را بصورت یک تابع در یک متغیر ذخیره کنیم:

ali = Person()

print(Person.greet)
>> <function Person.greet>

print(ali.greet)
>> <bound method Person.greet of <__main__.Person object>>

ali.greet()
>> Hello

احتمالا متوجه پارامتر self در تعریف کلاس شده‌اید، اما در فراخوانی تابع مقداری بعنوان آرگومان به تابع پاس داده نشده است. این بدان دلیل است که هنگامی یک شی تابع خود را فراخوانی می‌کند، خود شی به عنوان اولین آرگومان منتقل می‌شود. این یعنی ()ali.great به (ali)Person.great تبدیل می‌شود. 

در پایتون تمامی توابع در تعریف کلاس باید self را به عنوان اولین پارامتر داشته باشند

 

تابع سازنده در کلاس

در پایتون توابع ویژه‌ای (Magic Method) نیز تعریف شده‌اند، که برای دسترسی به آنها می‌توانید از دستور dir(int) استفاده کنید. یکی از مهم‌ترین آنها تابع __init__ است. این تابع ویژه زمانی که یک شی از کلاس ایجاد می‌کنید فراخوانی می‌شود.

به این تابع در برنامه نویسی شی‌گرا (OOP) سازنده نیز گفته می‌شود. ما معمولاً از آن برای مقداردهی اولیه متغیرها استفاده می‌کنیم.

Image for post
Image for post

در مثال بالا، کلاس جدیدی برای نمایش اعداد مختلط تعریف کردیم. این کلاس دو تابع دارد، یکی تابع سازنده است که مقداردهی اولیه متغیرها را انجام می‌دهد (پیش‌فرض صفر است)،‌ و تابع get_data که برای نمایش صحیح عدد مختلط تعریف شده است. 

در اینجا نکته جالب توجه این است که ویژگی‌های یک شی را می‌توان در هنگام کار با آن بصورت منحصر بفردی ایجاد کرد. همانطور که مشاهده می‌کنید ما یک متغیر جدید به نام attr برای شی num2 ایجاد کردیم، اما این ویژگی برای num۱ ایجاد نشد.

در پایتون این امکان نیز وجود دارد که می‌توانیم خاصیت‌های یک شی و حتی خود شی را پاک کنیم. این کار با کلمه کلیدی del انجام می‌شود.

del num2.attr
print(num2.attr)

>> Error: AttributeError: 'ComplexNumber' object has no attribute 'attr'
del num2.get_data
print(num2.get_data)

>> Error: AttributeError: get_data
del num2
print(num2)

>> Error: NameError: name 'num2' is not defined

در ادامه چند مورد از توابع ویژه را بررسی خواهیم کرد:

__new__

در زبان‌های مانند جاوا برای ایجاد یک شی جدید از یک کلاس از کلمه کلیدی new استفاده می‌کنند. در پایتون نیز  __new__ بصورت ضمنی پیش از __init__ فراخوانی می‌شود.

class Employee:
	def __new__(cls):
        print ("__new__ magic method is called")
        return object.__new__(cls)

	def __init__(self, name):
        print ("__init__ magic method is called")
        self.name = name
      
 
em1 = new Employee('Ali')
>> __new__ magic method is called
>> __init__ magic method is called

__str__ و __repr__ 

زمانی که یک شی را print می‌گیریم در واقع تابع __str__  را فراخوانی می‌کنیم. این دو تابع یک نسخه رشته‌ای از یک شی را باز می‌گردانند. 

print(num1) # or str(num1)
repr(num1) # or simply num1

>> <__main__.ComplexNumber object at 0x7f9ac04cb130>

در پایتون از __str__ یا ()str برای ایجاد خروجی استفاده می‌شود که توسط انسان قابل خواندن است و باید برای کاربران نهایی باشد. اما __repr__ یا ()repr با هدف رفع اشکال و توسعه می‌باشد.

برای روشن شدن این موضوع به مثال زیر که بر روی یک شی از ماژول datetime اعمال شده توجه کنید:

Image for post
Image for post

البته این دو تابع ویژه را می‌توانید در کلاس‌های خود بصورت اختصاصی تعریف کنید:

باز تعریف کردن repr و str در کلاس
باز تعریف کردن repr و str در کلاس
باز تعریف کردن repr و str در کلاس

__add__ ،__sub__ ،__mul__ و __truediv__ یا  __floordiv__ 

همچنین این قابلیت نیز وجود دارد که بتوانیم عملگرها ریاضی مانند + ، - ، × ، / و // را در کلاس‌ها باز تعریف کنیم.

Image for post
Image for post

برای مطالعه بیشتر در مورد توابع ویژه در پایتون به اینجا مراجع کنید

وراثت یا Inheritance

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

سینتکس ارث‌بری در پایتون بصورت زیر است:

class BaseClass:
  Body of base class
  
class DerivedClass(BaseClass):
  Body of derived class

انتقال وراثت می‌تواند بصورت غیر مستقیم نیز باشد به اینصورت که فرزند یک کلاس، خود والد کلاس دیگر باشد. به این نوع از وراثت، اصطلاحا وراثت چندسطحی (Multilevel Inheritance) می‌گویند. 

class A:
   def amethod(self):
       print("class A method")
       
class B(A):
   def bmethod(self):
       print("class B method")
       
class C(B):
   def cmethod(self):
       print("class C method")
       
       
 myC = C()
 myC.amethod()
 myC.bmethod()
 myC.cmethod()
 
 >> class A method
 >> class B method
 >> class C method

همچنین این امکان نیز وجود دارد که یک کلاس چندین والد داشته باشد، به این از وراثت اصطلاحا وراثت چندگانه (Multiple Inheritance) می‌گویند.

class A:
   def amethod(self):
       print("class A method")
       
class B:
   def bmethod(self):
       print("class B method")
       
class C(A, B):
   def cmethod(self):
       print("class C method")
       
       
 myC = C()
 myC.amethod()
 myC.bmethod()
 myC.cmethod()
 
 >> class A method
 >> class B method
 >> class C method

در تصویر زیر بصورت شماتیک این نوع از وراثت نمایش داده شده است:

وراثت چندسطحی و چندگانه
وراثت چندسطحی و چندگانه
وراثت چندسطحی و چندگانه

 

Class Method و Static Method

تا به اینجا توابعی که در کلاس تعریف کردیم از نوع توابعی بوده‌اند که پس از تولید یک شی از کلاس توسط شی فراخوانی می‌شدند، که اصطلاحا به آن‌ها توابع نمونه (Instance Methods) می‌گویند. در این نوع توابع اولین آرگومان self باید باشد که نشان‌دهنده شی ایست که از کلاس تولید شده. اما نوع دیگری از توابع در کلاس‌های پایتون تعریف می‌شوند مه در این قسمت توضیح می‌دهم:

تابع کلاس (Class Method)

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

اولین پارامتر در تابع کلاس، یک کلاس است که معمولا از cls برای نمایش این آرگومان الزامی استفاده می‌شود. برای تعریف تابع کلاس نیست تنها کافی است از دکوراتور classmethod@ قبل از تعریف تابع استفاده کرد. سینتکس این نوع از توابع بصورت زیر است:

class C:
    @classmethod
    def fun(cls, arg1, arg2, ...):

در مثال زیر یک تابع کلاس تعریف شده است:

Image for post
Image for post

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

class Student:

    @classmethod
    def get_from_string(cls, name_string: str):

    @classmethod
    def get_from_json(cls, json_obj):

    @classmethod
    def get_from_excle(cls, excle_file):
    
    @classmethod
    def get_from_audio_record(cls, audio):

الگوی متد کارخانه یکی از الگوهای طراحی برنامه‌نویسی شی‌گراست که به کلاس اجازه می دهد تا نحوه نمونه‌سازی‌ها به زیر کلاس‌ها تحویل داده شود.

تابع استاتیک (Static Method)

این نوع از توابع همانند توابع کلاس عمل می‌کنند با این تفاوت که هیچ آرگومان الزامی ندارند و ما می‌توانیم با کمک دکوراتور staticmethod@ قبل از تعریف تابع آن‌ها را تعریف کنیم.

Image for post
Image for post

در کلاس Student تابع استاتیک suitable_age به عنوان یک تابع کمکی بررسی می‌کند که آیا سن یک فرد برای دانش‌آموز بودن مناسب است. مکانیسم توابع استاتیک به ما کمک می‌کند تا کلاس‌های خود را بهتر مدیریت کنیم. اگر به برخی از پروژه‌های معروف منبع باز پایتون نگاه کنیم، بسیاری از توابع استاتیک به عنوان توابع کمکی در یک کلاس وجود دارند.

Properties

property راهی برای تعریف دسترسی به خاصیت‌های یک شی می‌باشد. برای این منظور از دکوراتور property@ بر یک تابع استفاده می‌شود، که به این معنی است که زمانیکه یک خاصی از یک شی صدا زده می‌شود بجای آن این تابع فراخوانی می‌شود.

property همچنین می‌تواند همراه با getter/setter تعریف شود       

Image for post
Image for post
Image for post
Image for post

همانطور که در کلاس فوق مشاهده می‌کنید درصورتیکه دما در این کلاس مقدار دهی شود تابع setter دما صدا زده می‌شود. در صورتیکه هم مقدار خاصیت دما در شی صدا زده شود تابع getter دما فراخوان می‌شود.


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