Skip to content

Commit 8a60e62

Browse files
author
Saeid Darvish
committed
l21: working on Data Classe
1 parent 6c59e52 commit 8a60e62

1 file changed

Lines changed: 109 additions & 53 deletions

File tree

lessons/l21.rst

Lines changed: 109 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,86 @@
2424
----
2525

2626

27+
Decorators
28+
----------------------------
29+
30+
از درس سیزدهم با مفهوم Decoratorها و نیز کاربرد آن‌ها به همراه تابع در زبان برنامه‌نویسی پایتون آشنا شده‌ایم، در این بخش به بررسی Decoratorهابه همراه کلاس‌ها و متدها می‌پردازیم.
31+
32+
علاوه بر اینکه با استفاده از کلاس می‌توان یک Decorator ایجاد کرد، از Decorator‌ها نیز می‌توان بر روی کلاس یا متدهای داخل یک کلاس بهره گرفت. در ادامه به بررسی این موارد می‌پردازیم.
33+
34+
35+
قراردادن Decorator بر روی متد
36+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37+
38+
این کار همانند قراردادن Decorator بر روی تابع می‌باشد (درس سیزدهم) و تفاوتی ندارد. پیش‌تر نیز از Decoratorهایی همچون ``classmethod@`` یا ``staticmethod@`` بر روی متدها استفاده می‌کردیم. به مثالی در همین زمینه توجه نمایید:
39+
40+
41+
.. code-block:: python
42+
:linenos:
43+
44+
import functools
45+
46+
def debug(func):
47+
"""Print the function signature and return value
48+
Source: https://realpython.com/primer-on-python-decorators/#debugging-code"""
49+
50+
@functools.wraps(func)
51+
def wrapper_debug(*args, **kwargs):
52+
args_repr = [repr(a) for a in args]
53+
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
54+
signature = ", ".join(args_repr + kwargs_repr)
55+
print(f"Calling {func.__name__}({signature})")
56+
value = func(*args, **kwargs)
57+
print(f"{func.__name__!r} returned {value!r}")
58+
return value
59+
return wrapper_debug
60+
61+
62+
63+
class Sample:
64+
65+
@debug
66+
def __init__(self, x=0, y=0):
67+
self.x = x
68+
self.y = y
69+
70+
71+
sample = Sample(5, y=6)
72+
73+
::
74+
75+
Calling __init__(<__main__.Sample object at 0x7fd96ddec8d0>, 5, y=6)
76+
'__init__' returned None
77+
78+
در نمونه کد بالا یک Decorator با نام ``debug`` ایجاد گردیده است (Decorator درس سیزدهم و f-string درس هفتم)، با قراردادن این Decorator بر روی یک تابع یا متد: نام تابع، آرگومان‌های ارسال شده و همچنین مقدار خروجی تابع را بر روی خروجی نمایش می‌دهد.
79+
80+
81+
82+
قراردادن Decorator بر روی کلاس
83+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
84+
85+
86+
87+
کلاس به عنوان Decorator
88+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
89+
90+
91+
92+
Descriptors
93+
----------------------------
94+
95+
96+
97+
Properties
98+
----------------------------
99+
100+
101+
102+
27103
Data Classes
28104
----------------------------
29105

30-
از **نسخه 3.7 پایتون** یک ویژگی جالب به پایتون اضافه گردید. دیتا کلاس **(Data Class)**، در واقع یک سینتکسی ساده‌سازی شده برای ایجاد کلاس‌هایی که معمولا تنها حاوی Instance Attribute می‌باشند. این نوع کلاس با استفاده از دکوراتور ``dataclass@`` از ماژول ``dataclasses`` ایجاد می‌گردد [`اسناد پایتون <https://docs.python.org/3/library/dataclasses.html>`__]. برای مثال کلاس زیر را در نظر بگیرید:
106+
از **نسخه 3.7 پایتون** یک ویژگی جالب به پایتون اضافه گردید. دیتا کلاس **(Data Class)** [`PEP 557 <https://www.python.org/dev/peps/pep-0557>`__]، در واقع یک سینتکسی ساده‌سازی شده برای ایجاد کلاس‌هایی که معمولا تنها حاوی Instance Attribute می‌باشند. این نوع کلاس با استفاده از دکوراتور ``dataclass@`` از ماژول ``dataclasses`` ایجاد می‌گردد [`اسناد پایتون <https://docs.python.org/3/library/dataclasses.html>`__]. برای مثال کلاس زیر را در نظر بگیرید:
31107

32108

33109
.. code-block:: python
@@ -60,7 +136,7 @@ Data Classes
60136

61137
در این نوع کلاس برای تعریف 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ها برای کنترل نوع داده‌ها در زمان توسعه برنامه یاری‌رسان باشد.
62138

63-
باید توجه داشت که طبق سند PEP 484 پیروی از اصول Type Hints در پایتون اجباری نبوده و نخواهد شد. ولی Data Class یک استثناست و در آن حتما می‌بایست Attributeها به شیوه شرح داده شده، تعریف گردند.
139+
باید توجه داشت که طبق سند PEP 484 پیروی از اصول Type Hints در پایتون اجباری نبوده و نخواهد شد. ولی Data Class یک استثناست و در آن حتما می‌بایست Attributeها به شیوه شرح داده شده، تعریف گردند و به آن‌ها فیلدهای (field) دیتا کلاس گفته می‌شود.
64140

65141
از آنجا که این نوع کلاس برای ایجاد یک کاربرد عمومی از کلاس‌ها توسعه یافته (نگهداری اطلاعات)، بنابراین بسیاری از عملیات‌ها در آن خودکارسازی شده تا پیاده‌سازی این کلاس ساده‌تر از هر کلاس دیگری باشد. برای مثال نیازی به پیاده‌سازی متد ``__init__`` نیست و این متد به صورت خودکار برای کلاس ما ایجاد می‌گردد. اکنون اگر بخواهیم دیتاکلاس مثال قبل را به صورت عادی پیاده‌سازی کنیم:
66142

@@ -167,7 +243,7 @@ Data Classes
167243

168244

169245

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__`` ارسال می‌کند و جزو فیلدهای دیتا کلاس قرار نمی‌دهد:
246+
از طریق ماژول ``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__`` ارسال می‌کند و **جزو فیلدهای دیتا کلاس قرار نمی‌دهد**:
171247

172248

173249
.. code-block:: python
@@ -193,85 +269,65 @@ Data Classes
193269
Student(score=100)
194270

195271

196-
تابع ``fields``
272+
تابع ``field`` و ``fields``
197273
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
198274

199-
200-
201-
202-
Decorators
203-
----------------------------
204-
205-
از درس سیزدهم با مفهوم Decoratorها و نیز کاربرد آن‌ها به همراه تابع در زبان برنامه‌نویسی پایتون آشنا شده‌ایم، در این بخش به بررسی Decoratorهابه همراه کلاس‌ها و متدها می‌پردازیم.
206-
207-
علاوه بر اینکه با استفاده از کلاس می‌توان یک Decorator ایجاد کرد، از Decorator‌ها نیز می‌توان بر روی کلاس یا متدهای داخل یک کلاس بهره گرفت. در ادامه به بررسی این موارد می‌پردازیم.
208-
209-
210-
قراردادن Decorator بر روی متد
211-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
212-
213-
این کار همانند قراردادن Decorator بر روی تابع می‌باشد (درس سیزدهم) و تفاوتی ندارد. پیش‌تر نیز از Decoratorهایی همچون ``classmethod@`` یا ``staticmethod@`` بر روی متدها استفاده می‌کردیم. به مثالی در همین زمینه توجه نمایید:
214-
275+
تابع ``fields`` از ماژول ``dataclasses`` یک شی از دیتا کلاس یا خود دیتا کلاس را از ورودی دریافت و یک تاپل حاوی تمام فیلد‌های آن بر می‌گرداند [`اسناد پایتون <https://docs.python.org/3/library/dataclasses.html#dataclasses.fields>`__]:
215276

216277
.. code-block:: python
217278
:linenos:
218279
219-
import functools
220-
221-
def debug(func):
222-
"""Print the function signature and return value
223-
Source: https://realpython.com/primer-on-python-decorators/#debugging-code"""
280+
from dataclasses import dataclass, InitVar, fields
224281
225-
@functools.wraps(func)
226-
def wrapper_debug(*args, **kwargs):
227-
args_repr = [repr(a) for a in args]
228-
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
229-
signature = ", ".join(args_repr + kwargs_repr)
230-
print(f"Calling {func.__name__}({signature})")
231-
value = func(*args, **kwargs)
232-
print(f"{func.__name__!r} returned {value!r}")
233-
return value
234-
return wrapper_debug
282+
@dataclass
283+
class Student:
284+
name: str
285+
score: int = 70
286+
age: InitVar[int] = 18
235287
236288
289+
obj = Student('saeid', 90, 20)
290+
print(obj)
291+
print(fields(obj))
237292
238-
class Sample:
293+
::
239294

240-
@debug
241-
def __init__(self, x=0, y=0):
242-
self.x = x
243-
self.y = y
295+
Student(name='saeid', score=90)
296+
(Field(name='name',type=<class 'str'>,default=<dataclasses._MISSING_TYPE object at 0x7f7e5c68cd68>,default_factory=<dataclasses._MISSING_TYPE object at 0x7f7e5c68cd68>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD), Field(name='score',type=<class 'int'>,default=70,default_factory=<dataclasses._MISSING_TYPE object at 0x7f7e5c68cd68>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD))
244297

245298

246-
sample = Sample(5, y=6)
299+
پیش‌تر گفتیم، Attributeهای داخل یک دیتا کلاس فیلد (Field) خوانده می‌شوند. خروجی بالا نمایش ساختار یک شی Field از دیتا کلاس می‌باشد [`اسناد پایتون <https://docs.python.org/3/library/dataclasses.html#dataclasses.field>`__]. در واقع متغیرهایی که داخل دیتا کلاس با سنتکس Variable Annotations تعریف می‌شوند، به صورت خودکار به فیلد (Field) تبدیل می‌شوند. فیلدها می‌توانند حاوی مقدار پیش‌فرض باشند (همانند فیلد ``score``). برای کاستن از حجم functionality داخل یک دیتا کلاس، ماژول ``dataclasses`` پایتون شامل تابعی است با نام ``field`` که توانایی و انعطاف زیادی در فراهم آوردن مقدار پیش‌فرض برای فیلدهای تعریف شده ایجاد می‌کند.
247300

248-
::
301+
یک شی فیلد شامل پارامترهایی است که از طریق تابع ``field`` قابل تنظیم هستند، البته به جز دو پارامتر زیر که از تعریف Variable Annotations استنباط می‌شوند:
249302

250-
Calling __init__(<__main__.Sample object at 0x7fd96ddec8d0>, 5, y=6)
251-
'__init__' returned None
252303

253-
در نمونه کد بالا یک Decorator با نام ``debug`` ایجاد گردیده است (Decorator درس سیزدهم و f-string درس هفتم)، با قراردادن این Decorator بر روی یک تابع یا متد: نام تابع، آرگومان‌های ارسال شده و همچنین مقدار خروجی تابع را بر روی خروجی نمایش می‌دهد.
304+
* ``name``: نام فیلد
254305

306+
* ``type``: نوع (type) فیلد
255307

308+
**تعریف مقدار پیش‌فرض برای یک فیلد با استفاده از تابع** ``field``::
256309

257-
قراردادن Decorator بر روی کلاس
258-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
310+
field(*, default=MISSING, default_factory=MISSING, repr=True, hash=None, init=True, compare=True, metadata=None)
259311

312+
* توجه:‌ همانطور که از مبحث Keyword-Only Arguments از درس دوازدهم به یاد داریم، فراخوانی این تابع تنها با استفاده از ارسال آرگومان به صورت نام=مقدار مجاز خواهید بود.
260313

261314

262-
کلاس به عنوان Decorator
263-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
315+
* ``default``: مقدار پیش‌فرض فیلد، در صورت عدم نیاز می‌بایست با مقدار ویژه ``MISSING`` مقداردهی گردد.
264316

317+
* ``default_factory``: یک موجودیت callable بدون آرگومان را دریافت می‌کند و در زمانی که به مقدار پیش‌فرض برای فیلد نیاز باشد، فراخوانی می‌گردد. در صورت عدم نیاز می‌بایست با مقدار ویژه ``MISSING`` مقداردهی گردد. به بیانی دیگر می‌توان با استفاده از این پارامتر،‌ یک تابع به فیلد اختصاص داد که مقدار یا مقادیر پیش‌فرضی را برای فیلد مورد نظر تولید نماید.
265318

319+
* توجه: در هر فیلد تنها یکی از دو پارامتر ``default`` یا ``default_factory`` می‌بایست حاوی مقداری غیر از ``MISSING`` باشد.
266320

267-
Descriptors
268-
----------------------------
269321

322+
* ``repr``, ``init``, ``compare``, ``hash``: در صورتی که هر کدام از این پارامتر‌ها برابر با مقدار ``True`` (پیش‌فرض) تنطیم گردند، فیلد مربوطه به متدهای ایجاد شده متناظر با هر پارامتر ارسال خواهد شد::
270323

324+
repr -->> __repr__ __str__
325+
init -->> __init__
326+
compare -->> __eq__ __ne__ __lt__ __le__ __gt__ __ge__
327+
hash -->> __hash__
271328

272-
Properties
273-
----------------------------
274329

330+
* توجه چنانچه مقدار ``compare`` برای ``True`` تنظیم گردد (حالت پیش‌فرض)،‌مقدار ``hash`` می‌بایست ``None`` (و نه ``False``) باشد، چرا که عملیات مقایسه دو شی دیگر به مقدار hash وابسته نبوده و از طریق متدهای تولید شده (__eq__ و غیره) انجام خواهد شد.
275331

276332

277333

0 commit comments

Comments
 (0)