|
1 | 1 | .. role:: emoji-size |
2 | 2 |
|
3 | 3 | .. meta:: |
4 | | - :description: کتاب آموزش زبان برنامه نویسی پایتون به فارسی، آموزش شی گرایی در پایتون، تعریف کلاس در پایتون، معرفی ساختار کلاس در پایتون، تعریف متد Method و صفت Attribute در کلاسهای پایتون، معرفی Constructor در کلاس پایتون، ایجاد شی و نمونه سازی در پایتون، OOP در پایتون |
| 4 | + :description: کتاب آموزش زبان برنامه نویسی پایتون به فارسی، آموزش شی گرایی در پایتون، تعریف کلاس در پایتون، معرفی ساختار کلاس در پایتون، تعریف متد Method و صفت Attribute در کلاسهای پایتون، معرفی Constructor در کلاس پایتون، ایجاد شی و نمونه سازی در پایتون، OOP در پایتون، شی hashable در پایتون، hash در پایتون، مقایسه دو شی در پایتون |
5 | 5 | :keywords: آموزش, آموزش پایتون, آموزش برنامه نویسی, پایتون, تابع, کتابخانه, پایتون, شی گرایی در پایتون |
6 | 6 |
|
7 | 7 |
|
@@ -630,6 +630,155 @@ Class Attribute |
630 | 630 | این نوع متد (Static Method) را میتوان هم با استفاده از نام کلاس دستیابی کرد (سطر ۱۷) و هم با استفاده از اشیای آن کلاس (سطر ۱۹)، در واقع دکوراتور ``staticmethod@`` کارهای لازم برای نادیده گرفتن شی و کلاس مربوط را انجام میدهد. |
631 | 631 |
|
632 | 632 |
|
| 633 | +مقدار Hash یک شی و کاربرد آن در پایتون |
| 634 | +--------------------------------------------- |
| 635 | + |
| 636 | +به صورت کلی یک **Hash** در واقع عددی است که در ازای دادهای مشخص برآورد میگردد. دادههای مشابه دارای مقدار hash یکسانی خواهند بود و از طرفی یک تغییر جزئی در داده منجر به تولید یک مقدار hash کاملا متفاوت میشود. مقدار hash از یک تابع هَش [`ویکیپدیا <https://en.wikipedia.org/wiki/Hash_function>`__] به دست میآید که مسئولیت آن تبدیل داده ورودی به hash رمزگذاری شده است. واضح است که تعداد دادهها میتواند بسیار بیشتر از تعداد مقادیر قابل تولید hash باشد، بنابراین دو داده ممکن است مقدار hash یکسان داشته باشند که به آن Hash collision میگویند. در واقع اگر دو شی hash یکسان داشته باشند، لزوماً دارای ارزش یکسانی نیستند (برابر نیستند). |
| 637 | + |
| 638 | + |
| 639 | +در زبان برنامهنویسی پایتون، تابع ``hash`` [`اسناد پایتون <https://docs.python.org/3/library/functions.html#hash>`__] یک شی hashable (قابل hash) را دریافت و مقدار hash آن را بر میگرداند:: |
| 640 | + |
| 641 | + >>> a = 5 |
| 642 | + >>> hash(a) |
| 643 | + 5 |
| 644 | + >>> hash(5) |
| 645 | + 5 |
| 646 | + |
| 647 | + >>> hash(999999999999999999) |
| 648 | + 999999999999999999 |
| 649 | + >>> hash(99999999999999999999999999999999999999) |
| 650 | + 244469275760665570 |
| 651 | + |
| 652 | + >>> a = 'saeid' |
| 653 | + >>> hash(a) |
| 654 | + 4007074958086188072 |
| 655 | + |
| 656 | + >>> hash('PYTHON') |
| 657 | + -6387242471900568301 |
| 658 | + |
| 659 | + >>> hash('PYTHoN') |
| 660 | + -6457932607787762593 |
| 661 | + |
| 662 | + |
| 663 | +* گاهی ممکن است مقدار hash برابر با یک عدد منفی محاسبه گردد، مقدار hash منفی نیز در پایتون معتبر میباشد. |
| 664 | + |
| 665 | +* با دوباره اجرا کردن برنامه یا اسکریپت ممکن است به نتایج دیگری از مقدار hash برسید. در واقع تظمین یکتایی مقدار hash تولید شده در پایتون تنها در ازای حیات هر proccess یا «اجرای برنامه» پابرجا خواهد بود. |
| 666 | + |
| 667 | + |
| 668 | +شی hashable |
| 669 | +~~~~~~~~~~~~~~~~~~~~~ |
| 670 | + |
| 671 | +گفتیم ورودی تابع ``hash`` پایتون میبایست یک شی hashable باید. **کدام اشیا در پایتون hashable هستند؟** تمامی اشیای که از نوع immutable (تغییرناپذیر - *مراجعه شود به بخش دستهبندی از درس هشتم*) هستند و همچنین اشیایی که متد خاص ``__hash__`` [`اسناد پایتون <https://docs.python.org/3/reference/datamodel.html#object.__hash__>`__] را پیادهسازی کرده باشند:: |
| 672 | + |
| 673 | + |
| 674 | + >>> class Sample: |
| 675 | + ... pass |
| 676 | + ... |
| 677 | + >>> obj = Sample() |
| 678 | + >>> hash(obj) |
| 679 | + -9223363243335467036 |
| 680 | + >>> obj.__hash__() |
| 681 | + -9223363243335467036 |
| 682 | + |
| 683 | +متد ``__hash__`` جزو متدهای خاص در پایتون میباشد و هر کلاسی که در پایتون ایجاد میکنید به صورت ضمنی یک پیادهسازی پیشفرض از این متد را شامل میشود. |
| 684 | + |
| 685 | + |
| 686 | +کاربرد hash در پایتون |
| 687 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 688 | + |
| 689 | +**۱) ساختار Hash table** [`ویکیپدیا <https://en.wikipedia.org/wiki/Hash_table>`__] |
| 690 | + |
| 691 | +ساختمان داده دو نوع **دیکشنری (dict)** و **مجموعه (set)** در زبان برنامهنویسی پایتون بر پایه Hash table ایجاد شده است. در نتیجه سرعت دستیابی عناصر در آنها بسیار بیشتر از دستیابی در شی لیست (List) میباشد. در نوع داده دیکشنری، hash کلیدها محاسبه و از آن برای دستیابی مقدار مربوطه استفاده میشود، برای همین است که **کلیدها در دیکشنری حتما میبایست از نوع hashable باشند** ولی برای مقادیر هیچ محدودیتی وجود ندارد. نوع داده مجموعه نیز **تنها میتواند شامل تعدادی شی hashable و یکتا (غیر تکراری) باشد**. |
| 692 | + |
| 693 | + |
| 694 | +**۲) مقایسه دو شی** |
| 695 | + |
| 696 | + |
| 697 | + |
| 698 | +.. tip:: |
| 699 | + |
| 700 | + * اگر دو شی با یکدیگر برابر باشند، آنگاه مقدار hash آنها نیز برابر خواهد بود. |
| 701 | + |
| 702 | + * اگر مقدار hash دو شی با یکدیگر برابر باشد، آنگاه **ممکن است** آن دو شی نیز با یکدیگر برابر باشند. |
| 703 | + |
| 704 | + |
| 705 | + |
| 706 | +طی دروس آینده با مبحث Operator Overloading آشنا خواهید شد ولی در اینجا تنها کافی است بدانید که هرگاه دو شی توسط عملگر ``==`` مقایسه گردند، متد ``__eq__`` [`اسناد پایتون <https://docs.python.org/3/reference/datamodel.html#object.__eq__>`__] به صورت خودکار فراخوانی خواهد شد. البته این متد نیز مانند باقی متدهای خاص پایتون، به صورت ضمنی یک پیادهسازی پیشفرض از خود دارد. در واقع خروجی این متد نتیجه مقایسه برابر بودن دو شی را برمیگرداند. |
| 707 | + |
| 708 | +بین دو متد ``__eq__`` و ``__hash__`` روابطی حاکم است که باید بدانیم: |
| 709 | + |
| 710 | + |
| 711 | +* اگر متد ``__eq__`` را پیادهسازی کنید ولی متد ``__hash__`` را خیر، **آنگاه اشیای کلاس مذکور hashable نخواهند بود**. |
| 712 | + |
| 713 | +* اگر متد ``__hash__`` را پیادهسازی کردهاید، آنگاه بهتر است متد ``__eq__`` را هم پیادهسازی نمایید. در غیر این صورت ممکن است در هنگام مقایسه اشیا خود دچار نتایج نامطلوب گردید. |
| 714 | + |
| 715 | +* در حالت پیشفرض پایتون، ``True`` بودن خروجی متد ``__eq__`` برای دو شی ``x`` و ``y`` یعنی ``x == y`` به معنی برقرار بودن دو شرط: ``x is y`` و ``hash(x) == hash(y)`` میباشد. |
| 716 | + |
| 717 | +* در حالت پیشفرض پایتون، تمام اشیای یک کلاس نابرابر و دارای مقدار hash متفاوت هستند، مگر اینکه ملاک مقایسه یک شی، خودش باشد. |
| 718 | + |
| 719 | +به دو نمونه کد زیر توجه نمایید: |
| 720 | + |
| 721 | + |
| 722 | +**مقایسه شی در حالت پیشفرض:** |
| 723 | + |
| 724 | +.. code-block:: python |
| 725 | + :linenos: |
| 726 | +
|
| 727 | + class Student: |
| 728 | + def __init__(self, name, score): |
| 729 | + self.name = name |
| 730 | + self.score = score |
| 731 | +
|
| 732 | + obj_1 = Student('Saeid', 70) |
| 733 | + obj_2 = Student('Saeid', 90) |
| 734 | +
|
| 735 | + print('Single Object :', obj_1 == obj_1) |
| 736 | + print('Same Objects :', obj_1 == Student('Saeid', 70)) |
| 737 | + print('Different Objects :', obj_1 == obj_2) |
| 738 | +
|
| 739 | +
|
| 740 | +:: |
| 741 | + |
| 742 | + Single Object : True |
| 743 | + Same Objects : False |
| 744 | + Different Objects : False |
| 745 | + |
| 746 | + |
| 747 | + |
| 748 | +**مقایسه شی به همراه شخصیسازی دو متد مذکور:** |
| 749 | + |
| 750 | + |
| 751 | +.. code-block:: python |
| 752 | + :linenos: |
| 753 | +
|
| 754 | + class Student: |
| 755 | + def __init__(self, name, score): |
| 756 | + self.name = name |
| 757 | + self.score = score |
| 758 | +
|
| 759 | + def __eq__(self, other): |
| 760 | + return self.name == other.name and self.score == other.score |
| 761 | +
|
| 762 | + def __hash__(self): |
| 763 | + return hash((self.name, self.score)) |
| 764 | +
|
| 765 | + obj_1 = Student('Saeid', 70) |
| 766 | + obj_2 = Student('Saeid', 90) |
| 767 | +
|
| 768 | + print('Single Object :', obj_1 == obj_1) |
| 769 | + print('Same Objects :', obj_1 == Student('Saeid', 70)) |
| 770 | + print('Different Objects :', obj_1 == obj_2) |
| 771 | +
|
| 772 | +:: |
| 773 | + |
| 774 | + Single Object : True |
| 775 | + Same Objects : True |
| 776 | + Different Objects : False |
| 777 | + |
| 778 | + |
| 779 | +در این مثال هر دو Attribute کلاس Student از نوع immutable بودند، بنابراین از خود آنها برای مقایسه و محاسبه مقدار hash استفاده کردیم. به هر حال منطق پیادهسازی این دو متد بر اساس مسئله مطرح شده، بر عهده برنامهنویس میباشد. حتی میتوانید از مقدار تابع ``()id`` یا همان ``id(self)`` بهره بگیرید. |
| 780 | + |
| 781 | + |
633 | 782 |
|
634 | 783 | | |
635 | 784 |
|
|
0 commit comments