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

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

شی و کلاس در پایتون

همان‌گونه که اطلاع دارید، پایتون یک زبان برنامه‌نویسی شی‌گرایی است که درست در نقطه مقابل زبان‌های برنامه‌نویسی رویه‌ای (Procedure Oriented Programming) قرار دارد که تمرکز اصلی آن‌ها روی توابع است. در زبان‌های برنامه‌نویسی شی‌گرایی تمرکز روی اشیا (Objects) است. در حالت کلی شی، مجموعه از داده‌ها (متغیرها، متدها و توابع) است که روی داده‌ها کار می‌کند و کلاس، یک پیش‌طرح (Blueprint) برای شی به شمار می‌رود. می‌توان به کلاس به عنوان یک طرح‌واره (پیش‌نمونه) از خانه نگاه کرد. کلاس، تمامی جزئیات پیرامون طبقات، درها، پنجره‌ها و دیگر موارد موردنیاز برای ساختن یک خانه را در خود جای می‌دهد. بر مبنای این ملزومات اولیه یک خانه ساخته می‌شود. در این‌جا خانه یک شی است. بسیاری از خانه‌ها را می‌توان بر اساس توضیحات (ملزومات اولیه) ساخت، با این‌حال، توسعه‌دهندگان می‌توانند اشیای زیادی را به یک کلاس اضافه کنند. به شی، نمونه‌ای از یک کلاس نیز گفته می‌شود و فرایند ساخت این شی را نمونه‌سازی (Instance) می‌گویند.

شرکت دانش بنیان بامداد ارائه می‌کند:

بوتکمپ برنامه‌نویسی پایتون (مقدماتی تا پیشرفته)

چگونه یک کلاس در پایتون تعریف کنیم؟

 

در این مطلب قصد داریم دو مفهوم بسیار مهم و پر کاربرد شی و کلاس در پایتون را بررسی کرده و به زبانی ساده نشان دهیم. همان‌گونه که تعریف توابع با کلیدواژه def آغاز می‌شود، در پایتون، یک کلاس با کلیدواژه class آغاز می‌شود. اولین رشته داک‌استرینگ (docstring ) نام دارد و توصیفی کوتاه درباره کلاس ارایه می‌کند. درست است که وجود داک‌استرینگ ضرورتی ندارد، اما پیشنهاد می‌شود از آن استفاده کنید. در قطعه کد زیر، یک تعریف کوتاه از کلاس را مشاهده می‌کنید.

class MyNewClass:

    ”’This is a docstring. I have created a new class”’

    pass

داک‌استرینگ، سرنام داکیومنتیشن استرینگ (Documentation String) است. داک‌استرینگ، رشته‌ای است که به عنوان اولین دستور در یک ماژول (Module)، تابع (Function)، کلاس (Class) یا تعریف متد (Method) قرار می‌گیرد. کاربر باید در داک‌استرینگ مشخص کند که یک تابع/کلاس چه کاری انجام می‌دهد. نقل قول سه‌تایی هنگام نوشتن داک‌استرینگ‌ها استفاده می‌شود. برای روشن شدن موضوع به قطعه کد زیر دقت کنید:

def double(num):

    “””Function to double the value”””

    return 2*num

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

>>> print(double.__doc__)

Function to double the value

هنگامی که کلاسی در پایتون را تعریف می‌کنید، کاری که یک کلاس انجام می‌دهد این است که یک فضای نام محلی (Local Namespace)، جدید ایجاد می‌کند که در آن، همه خصلت‌ها تعریف شده‌اند. خصلت‌ها می‌توانند داده یا تابع یا ترکیبی از هر دو حالت باشند. علاوه بر این، ممکن است در یک کلاس خصلت‌های خاصی وجود داشته باشند که با دو زیر خط (ــ) آغاز می‌شوند. به‌طور مثال، __doc__ یک داک‌استرینگ از آن کلاس به کاربر ارایه می‌کند. هنگامی که یک کلاس تعریف می‌شود، یک شی کلاس جدید با نام مشابه ساخته می‌شود. این شی کلاس امکان دسترسی به خصلت‌های مختلف مثل نمونه‌سازی اشیای جدید از آن کلاس را فراهم می‌کند.

class Person:

    “This is a person class”

    age = 10

    def greet(self):

        print(‘Hello’)

# Output: 10

print(Person.age)

# Output: <function Person.greet>

print(Person.greet)

# Output: “This is a person class”

print(Person.__doc__)

خروجی قطعه کد فوق به شرح زیر است:

10

<function Person.greet at 0x7fc78c6e8160>

This is a person class

‌چگونه یک شی در پایتون ایجاد کنیم؟

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

>>> harry = Person()

کاری که دستور بالا انجام می‌دهد این است که یک شی نمونه جدید به‌نام harry ایجاد می‌کند. می‌توان به خصلت‌های مختلف یک شی با استفاده از پیشوند نام شی دست پیدا کرد. خصلت‌ها ممکن است داده یا متد باشند. متدهای یک شی، توابع متناظری از آن کلاس هستند. هر شی تابع که یک کلاس خصیصه است، متدی برای اشیای آن کلاس تعریف می‌کند. به بیان دقیق‌تر، با توجه به این‌که Person.greet یک شی تابع (خصیصه کلاس) است، Person.greet شی متد است.

class Person:

    “This is a person class”

    age = 10

    def greet(self):

        print(‘Hello’)

# create a new object of Person class

harry = Person()

# Output: <function Person.greet>

print(Person.greet)

# Output: <bound method Person.greet of <__main__.Person object>>

print(harry.greet)

# Calling object’s greet() method

# Output: Hello

harry.greet()

خروجی قطعه کد فوق به شرح زیر است:

<function Person.greet at 0x7fd288e4e160>

<bound method Person.greet of <__main__.Person object at 0x7fd288e9fa30>>

Hello

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

در حالت کلی، فراخوانی یک متد با یک فهرست از n آرگومان، یکسان با فراخوانی تابع متناظر با یک فهرست آرگومان است که با قرار دادن شی متدها قبل از اولین آرگومان ساخته می‌شود. به همین دلیل، اولین آرگومان از تابع در کلاس باید خود شی باشد. رویکرد فوق به‌نام خود (Self) توصیف می‌شود. می‌توان آن‌را نام‌گذاری نیز کرد، با این‌حال پیشنهاد می‌شود از الگوهای استاندارد پیروی کنید. تا این بخش از مقاله باید با شی کلاس، شی نمونه، شی تابع، شی متد و تفاوت آن‌ها آشنا شده باشد.

شرکت دانش بنیان بامداد ارائه می‌کند: آموزش برنامه نویسی پایتون به کودکان

سازنده‌ها در پایتون

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

class ComplexNumber:

    def __init__(self, r=0, i=0):

        self.real = r

        self.imag = i

    def get_data(self):

        print(f'{self.real}+{self.imag}j’)

# Create a new ComplexNumber object

num1 = ComplexNumber(2, 3)

# Call get_data() method

# Output: 2+3j

num1.get_data()

# Create another ComplexNumber object

# and create a new attribute ‘attr’

num2 = ComplexNumber(5)

num2.attr = 10

# Output: (5, 0, 10)

print((num2.real, num2.imag, num2.attr))

# but c1 object doesn’t have attribute ‘attr’

# AttributeError: ‘ComplexNumber’ object has no attribute ‘attr’

print(num1.attr)

خروجی قطعه کد بالا به شرح زیر است:

2+3j

(5, 0, 10)

Traceback (most recent call last):

  File “<string>”, line 27, in <module>

    print(num1.attr)

AttributeError: ‘ComplexNumber’ object has no attribute ‘attr’

در قطعه کد بالا، کلاس جدیدی برای ارائه اعداد پیچیده تعریف شده است. این کلاس، دارای دو تابع است. __init__() برای مقداردهی اولیه به متغیرها که در حالت پیش‌فرض صفر هستند وgetData()  برای نمایش عدد به شیوه مناسب استفاده کرده است. نکته مهمی که باید درباره قطعه کد بالا به آن دقت کنید این است که خصلت‌های یک شی را می‌توان بر مبنای الگوی در هوا (fly) ایجاد کرد. یک خصلت جدید attr برای شی num2 ساخته و خوانده شد، اما این‌کار به معنای آن نیست که خصلتی برای شی num1  ساخته خواهد شد.

حذف خصلت‌ها و اشیا

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

>>> num1 = ComplexNumber(2,3)

>>> del num1.imag

>>> num1.get_data()

Traceback (most recent call last):

AttributeError: ‘ComplexNumber’ object has no attribute ‘imag’

>>> del ComplexNumber.get_data

>>> num1.get_data()

Traceback (most recent call last):

AttributeError: ‘ComplexNumber’ object has no attribute ‘get_data’

ما می‌توانیم خود شی را با استفاده از عبارت del نیز حذف کنیم:

>>> c1 = ComplexNumber(1,3)

>>> del c1

>>> c1

Traceback (most recent call last):

NameError: name ‘c1’ is not defined

البته در دنیای واقعی کار کمی پیچیده‌تر است. هنگامی کهc1=ComplexNumber(1,3)  اجرا می‌شود، یک نمونه شی جدید در حافظه ساخته می‌شود و نام c1 به آن متصل می‌شود. در دستور del c1، این اتصال حذف می‌شود و نام c1 از فضای نام متناظر حذف می‌شود. اگر هیچ نام دیگری به شی اتصال پیدا نشود، همچنان در حافظه باقی می‌ماند، اما پس از مدتی به‌طور خودکار تخریب می‌شود. این تخریب خودکار از اشیای بدون ارجاع در پایتون، بازیافت حافظه نامیده می‌شود و مانع از آن می‌شود تا حافظه بیهوده اشغال شده و از دست برود. رویکرد فوق درست در نقطه مقابل زبان‌های برنامه‌نویسی مثل سی قرار دارد که برنامه‌نویس به‌طور صریح و روشن باید اقدام به آزادسازی حافظه کند.

نویسنده: حمیدرضا تائبی

منبع:

https://www.programiz.com/python-programming/class