کل

کلاس در Python

49 تمرین

درباره‌ی کلاس

Classes داده را با رفتار ترکیب می‌کنند. از کلاس‌ها برای ساختن نسخه‌ها یا instances‌یی از داده و رفتارِ بسته‌بندی‌شده استفاده می‌شود که معمولاً با اسم objects شناخته می‌شوند. اشیاء می‌توانند موجودیت‌های دنیای واقعی (مثل ماشین‌ها یا گربه‌ها) را بازنمایی کنند، یا مفاهیم انتزاعی‌تر (مثل اعداد صحیح، وسایل نقلیه یا پستانداران). کلاس‌ها جزئی جدایی‌ناپذیر از رویکرد برنامه‌نویسی شیءگرا (OOP) هستند؛ رویکردی که از برنامه‌نویس می‌خواهد مسئله را به‌صورت یک یا چند شیء مدل‌سازی کند که با هم تعامل دارند، حالت را نگه می‌دارند و روی داده‌ها عمل می‌کنند.

کلاس

کلاس‌ها تعریفِ انواع شیءِ تازه‌اند و instances جدیدی از اشیاء از آن‌ها ساخته می‌شود. آن‌ها اغلب داده (که با اسم‌های fields، attributes، properties، data members یا variables شناخته می‌شود) را با کد یا توابع (که با اسم methods شناخته می‌شوند) که روی آن داده عمل می‌کنند، همراه می‌کنند. از این نظر، یک کلاس یک نقشه یا مجموعه‌ای از دستورهاست که می‌توان از آن بسیاری از اشیاء از یک نوع مشابه را ساخت و به کار برد. یک برنامه‌ی پیچیده می‌تواند کلاس‌های زیادی داشته باشد که هرکدام گونه‌های مختلفی از اشیاء را می‌سازند. فرایند ساختن و سفارشی‌سازی یک شیء از یک کلاس، instantiation (یا ساختن یک نمونه از کلاس) نامیده می‌شود.

تعریف یک کلاس در Python ساده است:

class MyClass:
    # Class body goes here

ویژگی کلاس

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

class MyClass:
    number = 5
    string = "Hello!"

می‌توان یک نمونه (شیء) از MyClass ساخت و آن را به یک اسم مقید کرد:

>>> new_object = MyClass()

# Class is instantiated and resulting object is bound to the "new_object" name (variable).
# Note: the object address 'at 0x15adc55b0' will vary by computer.
>>> new_object
<__main__.MyClass at 0x15adc55b0>

Class attributes میان همه‌ی اشیاء (یا نمونه‌هایی) که از یک کلاس ساخته می‌شوند مشترک‌اند و می‌توان از طریق dot notation به آن‌ها دسترسی داشت؛ یعنی یک . که بعد از اسم شیء و پیش از اسم ویژگی قرار می‌گیرد:

>>> new_object = MyClass()

# Accessing the class attribute "number" via dot-notation.
>>> new_object.number
5

# Accessing the class attribute "string" via dot-notation.
>>> new_object.string
'Hello!'

# Instantiating an additional object and binding it to the "second_new_object" name.
>>> second_new_object = MyClass()

>>> second_new_object
<__main__.MyClass at 0x15ad99970>

# Second_new_object shares the same class attributes as new_object.
>>> new_object.number == second_new_object.number
True

ویژگی‌های کلاس در بدنه‌ی خود کلاس، پیش از هر متد دیگری تعریف می‌شوند. آن‌ها متعلق به کلاس‌اند و همین باعث می‌شود میان نمونه‌ها مشترک باشند. چون این ویژگی‌ها میان همه‌ی نمونه‌های کلاس مشترک‌اند، می‌توان مقدارشان را مستقیماً از خود کلاس خواند و دستکاری کرد. تغییر مقدار ویژگی‌های کلاس، مقدار را برای همه‌ی اشیاء نمونه‌سازی‌شده از آن کلاس تغییر می‌دهد:

>>> obj_one = MyClass()
>>> obj_two = MyClass()

# Accessing a class attribute from an object.
>>> obj_two.number
5

# Accessing the class attribute from the class itself.
>>> MyClass.number
5

# Modifying the value of the "number" class attribute.
>>> MyClass.number = 27

# Modifying the "number" class attribute changes the "number" attribute for all objects.
>>> obj_one.number
27

>>> obj_two.number
27

داشتن مشتی شیء که همیشه داده‌شان همگام باشد، چندان مفید نیست. خوشبختانه، اشیاء ساخته‌شده از یک کلاس را می‌توان با instance attributes (یا خواص، متغیرها یا فیلدهای نمونه) خودشان سفارشی کرد. همان‌طور که از اسمشان پیداست، ویژگی‌های نمونه مخصوص هر شیء هستند و می‌توان مستقل از هم تغییرشان داد.

سفارشی‌سازی نمونه‌سازی شیء با __init__()

متد ویژه‌ی «dunder method» (کوتاه‌شده‌ی «double underscore method») یعنی __init__() برای سفارشی‌سازی نمونه‌های کلاس به کار می‌رود و می‌توان با آن ویژگی‌های نمونه یا خواص اشیاء را مقدار‌دهی اولیه کرد. به‌خاطر نقشش در مقدار‌دهی اولیه‌ی ویژگی‌های نمونه، از __init__() با اسم class constructor یا initializer هم یاد می‌شود. __init__() یک پارامتر اجباری به اسم self می‌گیرد که به شیء تازه‌ساخته‌شده اشاره دارد. سپس می‌توان داده‌ی ویژگی‌های نمونه یا خواص را، پس از پارامتر self، به‌عنوان آرگومان‌های __init__() فرستاد.

در پایین، MyClass اکنون ویژگی‌های نمونه‌ای به اسم location_x و location_y دارد. همان‌طور که می‌بینید، این دو ویژگی به اندیس‌های اول و دوم آرگومان location (یک تاپل) که به __init__() فرستاده شده، نسبت داده شده‌اند. ویژگی‌های location_x و location_y برای یک نمونه از کلاس اکنون هنگام نمونه‌سازی کلاس و ساخت شیء، مقدار‌دهی اولیه می‌شوند:

class MyClass:
    # These are class attributes, variables, or fields.
    number = 5
    string = "Hello!"

    # This is the class "constructor", with a "location" parameter that is a tuple.
    def __init__(self, location):

        # This is an instance or object property, attribute, or variable.
        # Note that we are unpacking the tuple argument into two separate instance variables.
        self.location_x = location[0]
        self.location_y = location[1]

# Create a new object "new_object_one", with object property (1, 2).
>>> new_object_one = MyClass((1, 2))

# Create a second new object "new_object_two" with object property (-8, -9).
>>> new_object_two = MyClass((-8, -9))

# Note that new_object_one.location_x and new_object_two.location_x two different values.
>>> new_object_one.location_x
1

>>> new_object_two.location_x
-8

توجه کنید که هنگام نمونه‌سازی MyClass در بالا فقط باید یک آرگومان بفرستید؛ Python هنگام فراخوانی کلاس، خودش self را می‌فرستد.

Advanced

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

>>> obj_one = MyClass()
>>> obj_two = MyClass()
>>> MyClass.string
'Hello!'

>>> obj_two.string = "Hi!"
>>> obj_two.string
'Hi!'

>>> obj_one.string
'Hello!'

حالا ببینید وقتی متغیر کلاس تغییر می‌کند چه اتفاقی می‌افتد:

>>> MyClass.string = "World!"
>>> obj_two.string

# obj_two.string has not changed
'Hi!'

>>> obj_one.string

# obj_one.string changed
'World!'

ویژگی string اکنون در مورد obj_two یک متغیر نمونه است، اما در obj_one همچنان یک متغیر کلاس می‌ماند.

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

class Demo:
    new_var = 3

    def add_two(self):
        self.new_var += 2

به محض اینکه <object>.add_two() فراخوانی شود و self.new_var += 2 خوانده شود، new_var از یک متغیر کلاس به متغیر نمونه‌ای با همان اسم تبدیل می‌شود.

این کار هنگام مقدار‌دهی اولیه می‌تواند مفید باشد، آنگاه که همه‌ی نمونه‌های یک کلاس به یک یا چند ویژگی نیاز داشته باشند که با مقدار یکسانی شروع شوند. با این حال، متغیر نمونه سپس متغیر کلاس را سایه می‌اندازد، و متغیر کلاس را از نمونه‌ای که در آن سایه افتاده، غیرقابل‌دسترس می‌کند. با توجه به این وضعیت، ممکن است تنظیم ویژگی‌های نمونه از متد __init__() به‌صورت self.<attribute> ایمن‌تر و روشن‌تر باشد.

متد

یک method یک function است که به خود کلاس (که با اسم متد کلاس شناخته می‌شود و در تمرینی بعدی به‌طور مفصل بحث خواهد شد) یا به یک نمونه از کلاس (شیء) مقید است. متدهایی که روی یک شیء یا نمونه عمل می‌کنند باید با self به‌عنوان پارامتر اول تعریف شوند. سپس می‌توانید بقیه‌ی پارامترها را همان‌طور که برای یک تابع «عادی» یا غیرمقید تعریف می‌کنید، تعریف کنید. متدهایی که روی یک کلاس عمل می‌کنند باید با دکوراتور @classmethod و (طبق قرارداد) با cls به‌عنوان پارامتر اول تعریف شوند. متدهای کلاس مستقیماً روی خود کلاس فراخوانی می‌شوند، بدون اینکه ابتدا شیئی از کلاس ساخته شود.

class MyClass:
    number = 5
    string = "Hello!"

    # Class constructor.
    def __init__(self, location):

        # Instance properties
        self.location_x = location[0]
        self.location_y = location[1]

    # Class method. Uses the @classmethod decorator, and cls as the first parameter.
    # Can be called without first making an instance of the class.
    @classmethod
    def change_string(cls, new_string):
        #Class attributes are referred to with cls.
        cls.string = new_string
        return cls.string

    # Instance method.  Note "self" as first parameter.
    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return self.location_x, self.location_y

مثل دسترسی به ویژگی، فراخوانی یک متد هم به همین سادگی است: گذاشتن یک . بعد از اسم شیء و پیش از اسم متد. متد فراخوانی‌شده نیازی به یک نسخه از شیء به‌عنوان پارامتر اول ندارد؛ Python خودش self را پر می‌کند:

class MyClass:
    number = 5
    string = "Hello!"

    def __init__(self, location):
        self.location_x = location[0]
        self.location_y = location[1]

    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return self.location_x, self.location_y

# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
>>> (test_object.location_x, test_object.location_y)
(3,7)

# Call change_location to increase location_x and location_y by 7.
>>> test_object.change_location(7)
(10, 14)

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

class MyClass:
    number = 5
    string = "Hello!"

    def __init__(self, location):
        self.location_x = location[0]
        self.location_y = location[1]

    # Alter instance variable location_x and location_y
    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return self.location_x, self.location_y

    # Alter class variable number for all instances from within an instance.
    def increment_number(self):
        # Increment the 'number' class variable by 1.
        MyClass.number += 1


>>> test_object_one = MyClass((0,0))
>>> test_object_one.number
5

>>> test_object_two = MyClass((13, -3))
>>> test_object_two.increment_number()
>>> test_object_one.number
6

جاخالی‌گذاشتن یا استاب‌کردن پیاده‌سازی با pass

در تمرین‌های مفهومی و استاب‌های تمرین‌های عملی قبلی، دیده‌اید که «کلیدواژه»ی pass در بدنه‌ی توابع به‌جای کد واقعی به کار رفته است.

کلیدواژه‌ی pass یک جای‌نگهدارِ از نظر نحوه‌ی نگارش معتبر است؛ جلوی این را می‌گیرد که Python برای یک تابع یا تعریف کلاس خالی، خطای نحوه‌ی نگارش پرتاب کند. در اصل، راهی است برای گفتن این جمله به مفسر Python: «نگران نباشید! بالاخره کد را اینجا می‌گذارم، فقط هنوز این کار را نکرده‌ام.»

class MyClass:
    number = 5
    string = "Hello!"

    def __init__(self, location):
        self.location_x = location[0]
        self.location_y = location[1]

    # Alter instance variable location_x and location_y.
    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return  self.location_x, self.location_y

    # Alter class variable number for all instances.
    def increment_number(self):
        # Increment the 'number' class variable by 1.
        MyClass.number += 1

    # This will compile and run without error, but has no current functionality.
    def pending_functionality(self):
       # Stubbing or place-holding the body of this method.
       pass
ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود

کلاس را یاد بگیرید

تمرین کردن قفل شده است

برای تمرین کلاس قفل 6 تمرین دیگر را باز کنید