Skip to content

Commit 6c59e52

Browse files
author
Saeid Darvish
committed
l21: start dataclass
1 parent 574ae48 commit 6c59e52

2 files changed

Lines changed: 170 additions & 1 deletion

File tree

lessons/l07.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,7 @@
10941094
تبدیل به نوع رشته
10951095
~~~~~~~~~~~~~~~~~~~~
10961096

1097-
برای تبدیل اشیایی از نوع دیگر به نوع رشته؛ کلاس ``()str`` [`اسناد پایتون <http://docs.python.org/library/functions.html#str>`__] و تابع ``()repr`` [`اسناد پایتون <http://docs.python.org/library/functions.html#repr>`__] وجود دارد. کلاس ``()str`` یک نمونه غیر رسمی (informal) از نوع شی رشته را برمی‌گرداند؛ غیر رسمی از این جهت که توسط آن جزییات شی رشته پنهان می‌شود. اما تابع ``()repr`` یک نمونه رسمی (official) از نوع رشته پایتون را برمی‌گرداند. کمی قبل‌تر راجب تفاوت خروجی ``print`` و حالت تعاملی صحبت کردیم؛ در واقع خروجی ``()str`` مناسب برای چاپ است و همانند ``print`` جزییات این نوع شی را ارایه نمی‌دهد در حالی که ``()repr`` به مانند حالت تعاملی یک ارايه (representation) کامل از شی رشته را برمی‌گرداند::
1097+
برای تبدیل اشیایی از نوع دیگر به نوع رشته؛ کلاس ``()str`` [`اسناد پایتون <http://docs.python.org/library/functions.html#func-str>`__] و تابع ``()repr`` [`اسناد پایتون <http://docs.python.org/library/functions.html#repr>`__] وجود دارد. کلاس ``()str`` یک نمونه غیر رسمی (informal) از نوع شی رشته را برمی‌گرداند؛ غیر رسمی از این جهت که توسط آن جزییات شی رشته پنهان می‌شود. اما تابع ``()repr`` یک نمونه رسمی (official) از نوع رشته پایتون را برمی‌گرداند. کمی قبل‌تر راجب تفاوت خروجی ``print`` و حالت تعاملی صحبت کردیم؛ در واقع خروجی ``()str`` مناسب برای چاپ است و همانند ``print`` جزییات این نوع شی را ارایه نمی‌دهد در حالی که ``()repr`` به مانند حالت تعاملی یک ارايه (representation) کامل از شی رشته را برمی‌گرداند::
10981098

10991099
>>> str(14)
11001100
'14'

lessons/l21.rst

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,175 @@
2727
Data Classes
2828
----------------------------
2929

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+
30199

31200

32201

0 commit comments

Comments
 (0)