|
27 | 27 | Data Classes |
28 | 28 | ---------------------------- |
29 | 29 |
|
| 30 | +از **نسخه 3.7 پایتون** یک ویژگی جالب به پایتون اضافه گردید. دیتا کلاس **(Data Class)**، در واقع یک سینتکسی سادهسازی شده برای ایجاد کلاسهایی که معمولا تنها حاوی Instance Attribute میباشند. این نوع کلاس با استفاده از دکوراتور ``dataclass@`` از ماژول ``dataclasses`` ایجاد میگردد [`اسناد پایتون <https://docs.python.org/3/library/dataclasses.html>`__]. برای مثال کلاس زیر را در نظر بگیرید: |
| 31 | + |
| 32 | + |
| 33 | +.. code-block:: python |
| 34 | + :linenos: |
| 35 | +
|
| 36 | + from dataclasses import dataclass |
| 37 | +
|
| 38 | + @dataclass |
| 39 | + class Student: |
| 40 | + name: str |
| 41 | + score: int |
| 42 | +
|
| 43 | + student = Student('Saeid', 70) |
| 44 | + print(student) |
| 45 | + print('-' * 30) |
| 46 | + print(student.name) |
| 47 | + print(student.score) |
| 48 | + print('-' * 30) |
| 49 | + print(Student('Saeid', 70) == Student('Saeid', 70)) |
| 50 | +
|
| 51 | +:: |
| 52 | + |
| 53 | + Student(name='Saeid', score=70) |
| 54 | + ------------------------------ |
| 55 | + Saeid |
| 56 | + 70 |
| 57 | + ------------------------------ |
| 58 | + True |
| 59 | + |
| 60 | + |
| 61 | +در این نوع کلاس برای تعریف Attributeها از سینتکس Variable Annotations [`PEP 526 <https://www.python.org/dev/peps/pep-0526/>`__] استفاده میشود. این سینتکس و در کل ذکر نوع داده در پایتون یا Type Hints [`PEP 484 <https://www.python.org/dev/peps/pep-0484/>`__] **موضوع درس بعدی است**. در این شیوه نوع متغیرها به صراحت ذکر میگردد. در حالت عادی تعریف یک متغییر در زبان برنامهنویسی پایتون به صورت ``var = value`` میباشد (درس ششم)، همانطور که میدانیم تاکنون هیچگاه در پایتون برای تعریف متغییر نیازی به ذکر صریح نوع داده نمیبود، **اکنون نیز نیازی نیست**، ولی از **نسخه 3.6 پایتون** میتوانیم اینکار را انجام دهیم، میتوانیم نوع داده را خودمان مشخص کنیم یا به اصطلاح آن نوع را annotation کنیم. سینتکس این عملیات به صورت ``var: annotation`` میباشد، این سینتکس مشخص میکند که متغییر var از نوع annotation میباشد. همچنین با استفاده از سینتکس ``var: annotation = value`` نیز میتوان همزمان عملیات انتساب و مقداردهی را نیز انجام داد. باید توجه داشت که تغییری در ساختار مفسر پایتون ایجاد نشده است!، بلکه صرفا سینتکس جدیدی اضافه شده که میتواند به ابزارهای شخصثالث (third party) همانند IDEها برای کنترل نوع دادهها در زمان توسعه برنامه یاریرسان باشد. |
| 62 | + |
| 63 | +باید توجه داشت که طبق سند PEP 484 پیروی از اصول Type Hints در پایتون اجباری نبوده و نخواهد شد. ولی Data Class یک استثناست و در آن حتما میبایست Attributeها به شیوه شرح داده شده، تعریف گردند. |
| 64 | + |
| 65 | +از آنجا که این نوع کلاس برای ایجاد یک کاربرد عمومی از کلاسها توسعه یافته (نگهداری اطلاعات)، بنابراین بسیاری از عملیاتها در آن خودکارسازی شده تا پیادهسازی این کلاس سادهتر از هر کلاس دیگری باشد. برای مثال نیازی به پیادهسازی متد ``__init__`` نیست و این متد به صورت خودکار برای کلاس ما ایجاد میگردد. اکنون اگر بخواهیم دیتاکلاس مثال قبل را به صورت عادی پیادهسازی کنیم: |
| 66 | + |
| 67 | + |
| 68 | +.. code-block:: python |
| 69 | + :linenos: |
| 70 | +
|
| 71 | + class Student: |
| 72 | +
|
| 73 | + def __init__(self, name, score): |
| 74 | + self.name = name |
| 75 | + self.score = score |
| 76 | +
|
| 77 | +
|
| 78 | + student = Student('Saeid', 70) |
| 79 | + print(student) |
| 80 | + print('-' * 30) |
| 81 | + print(student.name) |
| 82 | + print(student.score) |
| 83 | + print('-' * 30) |
| 84 | + print(Student('Saeid', 70) == Student('Saeid', 70)) |
| 85 | +
|
| 86 | +:: |
| 87 | + |
| 88 | + <__main__.Student object at 0x7f922a311518> |
| 89 | + ------------------------------ |
| 90 | + Saeid |
| 91 | + 70 |
| 92 | + ------------------------------ |
| 93 | + False |
| 94 | + |
| 95 | + |
| 96 | +با مقایسه این دو خروجی، مشاهده میشود که مقدار چاپ شی (سطر ۹) و نیز حاصل مقایسه دو شی (سطر ۱۴) با مقادیر یکسان متفاوت است. دلیل نیز پیشتر بیان شد، تعدادی متد خاص همانند ``__init__`` برای دیتا کلاسها پیادهسازی میشود که با پیادهسازی پیشفرض متفاوت بوده و بر نوع کاربرد این کلاسها و راحتی استفاده آنها تمرکز شده است. این پیادهسازی را میتوان به صورت زیر نمایش داد: |
| 97 | + |
| 98 | + |
| 99 | + |
| 100 | +.. code-block:: python |
| 101 | + :linenos: |
| 102 | +
|
| 103 | + class Student: |
| 104 | +
|
| 105 | + def __init__(self, name, score): |
| 106 | + self.name = name |
| 107 | + self.score = score |
| 108 | +
|
| 109 | + def __str__(self): |
| 110 | + return (f'{self.__class__.__name__}' |
| 111 | + f'(name={self.name!r}, score={self.score!r})') |
| 112 | +
|
| 113 | + def __eq__(self, other): |
| 114 | + return (self.name, self.score) == (other.name, other.score) |
| 115 | +
|
| 116 | +
|
| 117 | + student = Student('Saeid', 70) |
| 118 | + print(student) |
| 119 | + print('-' * 30) |
| 120 | + print(student.name) |
| 121 | + print(student.score) |
| 122 | + print('-' * 30) |
| 123 | + print(Student('Saeid', 70) == Student('Saeid', 70)) |
| 124 | +
|
| 125 | +:: |
| 126 | + |
| 127 | + Student(name='Saeid', score=70) |
| 128 | + ------------------------------ |
| 129 | + Saeid |
| 130 | + 70 |
| 131 | + ------------------------------ |
| 132 | + True |
| 133 | + |
| 134 | +از درس پیش با متد ``__eq__`` آشنا هستیم، متد ``__str__`` [`اسناد پایتون <https://docs.python.org/3/reference/datamodel.html#object.__str__>`__] نیز یکی دیگر از متدهای خاص پایتون میباشد و هنگامی که یک شی میخواهد به نوع str تبدیل گردد، به صورت خودکار فراخوانی میگردد (**تبدیل به نوع رشته - درس هفتم**)، به صورت مشابه متد ``__repr__`` [`اسناد پایتون <https://docs.python.org/3/reference/datamodel.html#object.__repr__>`__] نیز قابل پیاده سازی است. |
| 135 | + |
| 136 | + |
| 137 | +متد ``__post_init__`` |
| 138 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 139 | + |
| 140 | +دیتا کلاسها همچنین میتوانند شامل متد نیز باشند، چگونگی تعریف متد در دیتا کلاس تفاوتی با دیگر کلاسها ندارد. |
| 141 | + |
| 142 | +از طرفی میدانیم که متد ``__init__`` یک دیتا کلاس به صورت خودکار ایجاد میگردد و مرحله initialize شی از دستان ما خارج شده است. با این حال چنانچه اگر کلاس شامل متدی با نام ``__post_init__`` باشد، این متد پس از ``__init__`` به صورت خودکار فراخوانی میگردد: |
| 143 | + |
| 144 | +.. code-block:: python |
| 145 | + :linenos: |
| 146 | +
|
| 147 | + from dataclasses import dataclass |
| 148 | +
|
| 149 | + @dataclass |
| 150 | + class Student: |
| 151 | + name: str |
| 152 | + score: int |
| 153 | +
|
| 154 | + def __post_init__(self): |
| 155 | + print("__post_init__ got called:", self) |
| 156 | + if self.name == 'Saeed': |
| 157 | + self.name = 'Saeid' |
| 158 | +
|
| 159 | +
|
| 160 | + student = Student('Saeed', 70) |
| 161 | + print(student) |
| 162 | +
|
| 163 | +:: |
| 164 | + |
| 165 | + __post_init__ got called: Student(name='Saeed', score=70) |
| 166 | + Student(name='Saeid', score=70) |
| 167 | + |
| 168 | + |
| 169 | + |
| 170 | +از طریق ماژول ``dataclasses`` یک نوع یک annotation type جدید با نام ``InitVar`` در دسترس است. چنانچه در تعریف هر یک از Attributeها کلاس از این نوع استفاده کنیم، آن Attribute به عنوان پارامتر به متد ``__post_init__`` ارسال میگردد. باید توجه داشت که این نوع Attributeها به عنوان **Init-only variables** شناخته میشوند [`اسناد پایتون <https://docs.python.org/3/library/dataclasses.html#init-only-variables>`__] و مفسر پایتون آنها را صرفا به ``__post_init__`` ارسال میکند و جزو فیلدهای دیتا کلاس قرار نمیدهد: |
| 171 | + |
| 172 | + |
| 173 | +.. code-block:: python |
| 174 | + :linenos: |
| 175 | +
|
| 176 | +
|
| 177 | + @dataclass |
| 178 | + class Student: |
| 179 | + name: InitVar[str] |
| 180 | + score: int |
| 181 | +
|
| 182 | + def __post_init__(self, name): |
| 183 | + if name == 'Saeid': |
| 184 | + self.score = 100 |
| 185 | +
|
| 186 | +
|
| 187 | + student = Student('Saeid', 70) |
| 188 | + print(student) |
| 189 | +
|
| 190 | +
|
| 191 | +:: |
| 192 | + |
| 193 | + Student(score=100) |
| 194 | + |
| 195 | + |
| 196 | +تابع ``fields`` |
| 197 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 198 | + |
30 | 199 |
|
31 | 200 |
|
32 | 201 |
|
|
0 commit comments