Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8d79331

Browse filesBrowse files
author
Saeid Darvish
committed
l22: complete
1 parent 76ad868 commit 8d79331
Copy full SHA for 8d79331

File tree

Expand file treeCollapse file tree

1 file changed

+158
-6
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

1 file changed

+158
-6
lines changed
Open diff view settings
Collapse file

‎lessons/l22.rst‎

Copy file name to clipboardExpand all lines: lessons/l22.rst
+158-6Lines changed: 158 additions & 6 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ Class Annotations
479479
Data Classes
480480
----------------------------
481481

482-
از **نسخه 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>`__]. برای مثال کلاس زیر را در نظر بگیرید:
482+
از **نسخه 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>`__]. برای مثال کلاس زیر را در نظر بگیرید:
483483

484484

485485
.. code-block:: python
@@ -510,11 +510,11 @@ Data Classes
510510
True
511511

512512

513-
در این نوع کلاس برای تعریف 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ها برای کنترل نوع داده‌ها در زمان توسعه برنامه یاری‌رسان باشد.
513+
در این نوع کلاس برای تعریف Attributeها از سینتکس Variable Annotations [`PEP 526 <https://www.python.org/dev/peps/pep-0526/>`__] استفاده می‌شود.
514514

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

517-
از آنجا که این نوع کلاس برای ایجاد یک کاربرد عمومی از کلاس‌ها توسعه یافته (نگهداری اطلاعات)، بنابراین بسیاری از عملیات‌ها در آن خودکارسازی شده تا پیاده‌سازی این کلاس ساده‌تر از هر کلاس دیگری باشد. برای مثال نیازی به پیاده‌سازی متد ``__init__`` نیست و این متد به صورت خودکار برای کلاس ما ایجاد می‌گردد. اکنون اگر بخواهیم دیتاکلاس مثال قبل را به صورت عادی پیاده‌سازی کنیم:
517+
از آنجا که این نوع کلاس برای ایجاد یک کاربرد عمومی از کلاس‌ها توسعه یافته (نگهداری اطلاعات)، بنابراین بسیاری از عملیات‌ها در آن خودکارسازی شده تا پیاده‌سازی این کلاس ساده‌تر از هر کلاس دیگری باشد. برای مثال نیازی به پیاده‌سازی متد ``__init__`` نیست و این متد به صورت خودکار برای کلاس ما ایجاد می‌گردد (به لطف Type Hinting!). اکنون اگر بخواهیم دیتاکلاس مثال قبل را به صورت عادی پیاده‌سازی کنیم:
518518

519519

520520
.. code-block:: python
@@ -545,7 +545,7 @@ Data Classes
545545
False
546546

547547

548-
با مقایسه این دو خروجی، مشاهده می‌شود که مقدار چاپ شی (سطر ۹) و نیز حاصل مقایسه دو شی (سطر ۱۴) با مقادیر یکسان متفاوت است. دلیل نیز پیشتر بیان شد،‌ تعدادی متد خاص همانند ``__init__`` برای دیتا کلاس‌ها پیاده‌سازی می‌شود که با پیاده‌سازی پیش‌فرض متفاوت‌ بوده و بر نوع کاربرد این کلاس‌ها و راحتی استفاده آن‌ها تمرکز شده است. این پیاده‌سازی را می‌توان به صورت زیر نمایش داد:
548+
با مقایسه این دو خروجی، مشاهده می‌شود که مقدار چاپ شی (سطر ۹) و نیز حاصل مقایسه دو شی (سطر ۱۴) با مقادیر یکسان، متفاوت است. دلیل نیز پیشتر بیان شد،‌ تعدادی متد خاص همانند ``__init__`` برای دیتا کلاس‌ها به صورت خودکار تولید می‌شوند که با پیاده‌سازی پیش‌فرض متفاوت‌ بوده و بر نوع کاربرد این کلاس‌ها و راحتی استفاده تمرکز شده است. این پیاده‌سازی را می‌توان به صورت زیر نمایش داد:
549549

550550

551551

@@ -586,6 +586,18 @@ Data Classes
586586
از درس پیش با متد ``__eq__`` آشنا هستیم، متد ``__str__`` [`اسناد پایتون <https://docs.python.org/3/reference/datamodel.html#object.__str__>`__] نیز یکی دیگر از متدهای خاص پایتون می‌باشد و هنگامی که یک شی می‌خواهد به نوع str تبدیل گردد، به صورت خودکار فراخوانی می‌گردد (**تبدیل به نوع رشته - درس هفتم**)، به صورت مشابه متد ``__repr__`` [`اسناد پایتون <https://docs.python.org/3/reference/datamodel.html#object.__repr__>`__] نیز قابل پیاده سازی است.
587587

588588

589+
بهتر است مقداردهی اولیه اشیای دیتاکلاس‌ها را به روش **نام=مقدار** انجام دهید (هنگام نمونه‌سازی)، در غیر این صورت اگر ترتیب تعریف فیلدها در کلاس را از بالا به پایین در نظر بگیریم، آنگاه ترتیب قرار گرفتن پارامترها در متد ``__init__`` که قرار است تولید شود، با حفظ ترتیب، از چپ به راست خواهند بود.
590+
591+
به همین دلیل می‌بایست در ترتیب قرارگرفتن فیلدهایی که دارای مقدار پیش‌فرض هستند دقت کرد و آن‌ها را جزو فیلد‌های انتهایی درنظر گرفت. چرا که تعریف متد ``__init__`` با خطا مواجه می‌گردد. از تعریف توابع به یاد داریم که پس از پارامتر با مقدار پیش‌فرض نمی‌تواند پارامتر بدون مقدار پیش‌فرض قرار بگیرد!
592+
593+
594+
595+
Type Hinting
596+
~~~~~~~~~~~~~~~~~~~~~~
597+
598+
تنها این Attributeهای یک دیتا کلاس است که می‌بایست بر اساس قوانین سینتکس Type Hinting نوشته شوند. در این بین برای درج Class Attributeها نیز می‌بایست حتما از ``ClassVar`` استفاده گردد، در غیر این صورت آن Attribute در حکم Instance Attribute خواهد بود.
599+
600+
589601
متد ``__post_init__``
590602
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
591603

@@ -695,7 +707,7 @@ Data Classes
695707
* توجه: در هر فیلد تنها یکی از دو پارامتر ``default`` یا ``default_factory`` می‌بایست حاوی مقداری غیر از ``MISSING`` باشد.
696708

697709

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

700712
repr -->> __repr__ __str__
701713
init -->> __init__
@@ -706,6 +718,146 @@ Data Classes
706718
* توجه چنانچه مقدار ``compare`` برای ``True`` تنظیم گردد (حالت پیش‌فرض)،‌مقدار ``hash`` می‌بایست ``None`` (و نه ``False``) باشد، چرا که عملیات مقایسه دو شی دیگر به مقدار hash وابسته نبوده و از طریق متدهای تولید شده (__eq__ و غیره) انجام خواهد شد.
707719

708720

721+
* ``metadata``: می‌توان اطلاعات اضافی و دلخواه پیرامون فیلد را در قالب یک شی دیکشنری به این پارامتر ارسال کرد.
722+
723+
724+
725+
به نمونه کد زیر توجه نمایید:
726+
727+
.. code-block:: python
728+
:linenos:
729+
730+
from dataclasses import dataclass, field, fields
731+
from typing import List
732+
733+
734+
def get_default_books():
735+
return []
736+
737+
738+
@dataclass
739+
class Book:
740+
id: int
741+
name: str = field(compare=False)
742+
743+
744+
@dataclass
745+
class Author:
746+
id: int
747+
name: str = field(compare=False, metadata={'coding': 'UTF-8'})
748+
books: List[Book] = field(default_factory=get_default_books, compare=False)
749+
750+
751+
752+
author = Author(id=1, name='Saeid')
753+
print(author)
754+
print(fields(author))
755+
756+
757+
::
758+
759+
Author(id=1, name='Saeid', books=[])
760+
(Field(name='id',type=<class 'int'>,default=<dataclasses._MISSING_TYPE object at 0x7f5e66a58e48>,default_factory=<dataclasses._MISSING_TYPE object at 0x7f5e66a58e48>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD), Field(name='name',type=<class 'str'>,default=<dataclasses._MISSING_TYPE object at 0x7f5e66a58e48>,default_factory=<dataclasses._MISSING_TYPE object at 0x7f5e66a58e48>,init=True,repr=True,hash=None,compare=False,metadata=mappingproxy({'coding': 'UTF-8'}),_field_type=_FIELD), Field(name='books',type=typing.List[__main__.Book],default=<dataclasses._MISSING_TYPE object at 0x7f5e66a58e48>,default_factory=<function get_default_books at 0x7f5e66bcb1e0>,init=True,repr=True,hash=None,compare=False,metadata=mappingproxy({}),_field_type=_FIELD))
761+
762+
763+
Immutable Data Classes
764+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
765+
766+
دکوراتور ``dataclass@`` چندین پارامتر با مقدار پیش‌فرض دارد که به شرح زیر می‌باشند [`اسناد پایتون <https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass>`__]::
767+
768+
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
769+
class Sample:
770+
...
771+
772+
773+
* ``init``: اگر ``True`` باشد، متد ``__init__`` تولید می‌شود.
774+
775+
* ``repr``: اگر ``True`` باشد، متد ``__repr__`` تولید می‌شود.
776+
777+
* ``order``: اگر ``True`` باشد، متدهای ``__gt__`` ،``__le__`` ،``__lt__`` و ``__ge__`` تولید می‌شوند.
778+
779+
* ``unsafe_hash``: اگر ``False`` باشد، آنگاه بر اساس مقادیر ``eq`` ،``init`` و ``frozen`` و شرایط موجود یک متد ``__hash__`` مناسب تولید می‌شود.
780+
781+
782+
**frozen**
783+
784+
چنانچه این پارامتر برابر ``True`` تنظیم گردد، دیتا کلاس Immutable (غیرقابل تغییر) خواهد شد و دیگر نمی‌توان مقدار هیچکدام از فیلدهای اشیای آن را پس از نمونه‌سازی تغییر داد، این رفتار در موارد بسیاری می‌تواند مفید باشد:
785+
786+
787+
788+
.. code-block:: python
789+
:linenos:
790+
791+
from dataclasses import dataclass
792+
793+
@dataclass(frozen=True)
794+
class Position:
795+
name: str
796+
lon: float = 0.0
797+
lat: float = 0.0
798+
799+
pos = Position('Tehran', 35.6, 51.5)
800+
801+
print(pos.name)
802+
print('-' * 30)
803+
pos.name = 'Qazvin'
804+
805+
::
806+
807+
Tehran
808+
------------------------------
809+
Traceback (most recent call last):
810+
File "sample.py", line 13, in <module>
811+
pos.name = 'Qazvin'
812+
File "<string>", line 3, in __setattr__
813+
dataclasses.FrozenInstanceError: cannot assign to field 'name'
814+
815+
816+
817+
وراثت (Inheritance)
818+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
819+
820+
دیتا کلاس‌ها می‌توانند از یکدیگر ارث‌بری داشته باشند:
821+
822+
823+
.. code-block:: python
824+
:linenos:
825+
826+
from dataclasses import dataclass
827+
828+
829+
@dataclass
830+
class Person:
831+
name: str
832+
833+
834+
@dataclass
835+
class Friend(Person):
836+
city: str
837+
838+
def say_hi(self):
839+
print(f'Hi {self.name}')
840+
841+
842+
f = Friend(city='Tehran', name='Armin')
843+
f.say_hi()
844+
845+
f = Friend('Tehran', 'Armin')
846+
f.say_hi()
847+
848+
::
849+
850+
Hi Armin
851+
Hi Tehran
852+
853+
854+
بهتر است مقداردهی اولیه اشیای دیتاکلاس‌ها را به روش **نام=مقدار** انجام دهید، در غیر این صورت باید بدانید در هنگام ارث‌بری ابتدا فیلدهای supperclass مقداردهی می‌شوند! در نتیجه می‌توان تعریف متد ``__init__`` برای کلاس ``Friend`` را برابر با تعریف زیر فرض کرد::
855+
856+
857+
def __init__(self, name, city):
858+
859+
به همین دلیل نیز اگر یکی از فیلدهای supperclass دارای مقدار پیش‌فرض باشد، می‌بایست فیلدهای subclass نیز دارای مقدار پیش‌فرض باشند. چرا که تعریف متد ``__init__`` با خطا مواجه می‌گردد. از تعریف توابع به یاد داریم که پس از پارامتر با مقدار پیش‌فرض نمی‌تواند پارامتر بدون مقدار پیش‌فرض قرار بگیرد!
860+
709861

710862
|
711863

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.