Skip to content

Commit d84424e

Browse files
author
Saeid Darvish
committed
l17: add hashable objects
1 parent 8a60e62 commit d84424e

1 file changed

Lines changed: 150 additions & 1 deletion

File tree

lessons/l17.rst

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.. role:: emoji-size
22

33
.. meta::
4-
:description: کتاب آموزش زبان برنامه نویسی پایتون به فارسی، آموزش شی گرایی در پایتون، تعریف کلاس در پایتون، معرفی ساختار کلاس در پایتون، تعریف متد Method و صفت Attribute در کلاس‌های پایتون، معرفی Constructor در کلاس پایتون، ایجاد شی و نمونه سازی در پایتون، OOP در پایتون
4+
:description: کتاب آموزش زبان برنامه نویسی پایتون به فارسی، آموزش شی گرایی در پایتون، تعریف کلاس در پایتون، معرفی ساختار کلاس در پایتون، تعریف متد Method و صفت Attribute در کلاس‌های پایتون، معرفی Constructor در کلاس پایتون، ایجاد شی و نمونه سازی در پایتون، OOP در پایتون، شی hashable در پایتون، hash در پایتون، مقایسه دو شی در پایتون
55
:keywords: آموزش, آموزش پایتون, آموزش برنامه نویسی, پایتون, تابع, کتابخانه, پایتون, شی گرایی در پایتون
66

77

@@ -630,6 +630,155 @@ Class Attribute
630630
این نوع متد (Static Method) را می‌توان هم با استفاده از نام کلاس دستیابی کرد (سطر ۱۷) و هم با استفاده از اشیای آن کلاس (سطر ۱۹)، در واقع دکوراتور ``staticmethod@`` کارهای لازم برای نادیده گرفتن شی و کلاس مربوط را انجام می‌دهد.
631631

632632

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

634783
|
635784

0 commit comments

Comments
 (0)