GreatBahram
GreatBahram
خواندن ۳ دقیقه·۶ سال پیش

پایتونیک - برابری یا هویت مساله این است!

مقدمه

سلام، امیدوارم که مثل همیشه خوش و خرم باشید. درمجموعه پایتونیک هدف‌مون این بود که راجعبه چیزهایی صحبت کنیم که باعث میشن یک قدم خوانایی‌ کدمون بالاتر بره. در این قسمت از پایتونیک سراغِ یک مبحث خیلی پایه‌ای رفتیم که در عین سادگی‌ِ میتونه باعث افزایشِ خوانایی کد‌مون بشه. چند وقت پیش مطلبی جالبی رو خوندم که تو دلِ اون مطلب یکم راجعبه برابری و هویت متغییرها در پایتون بحث شده بود. دوست داشتم اون مطلب رو با شما هم به اشتراک بذارم.

انتظار داریم در انتهای این قسمت به این نتیجه برسیم که درست‌ترین حالتِ استفاده از عملگر == و is به چه صورت هست و این اصل رو به صورت روزمره بکار ببریم.


برابری Equality

توی پایتون وقتی می‌خواهیم ببینم دو آبجکت با هم برابر هستند یا خیر، از عملگرهای == و =! استفاده می‌کنیم.

هویت Identity

در مقابل‌ این عملگرها، دوتا عملگر دیگه هم داریم که is و is not هستند. is بررسی می‌کنه که آیا این آبجکت دقیقا یک آبجکت هستند یا نه. دقت کنید، ما داریم راجعبه چیزی فراتر از برابری حرف می‌زنیم. وقتی می‌گیم یکی هستند یا نه به صورت خیلی تخصصی یعنی اون دو متغییر دقیقاً در یک مکان از حافظه قرار گرفتند (ارجاعی به یک خونه از حافظه هستند)، یعنی این دو متغییر صرفا دو تا اسم هستند که دارند به یک جا اشاره می‌کنند.

بریم با یک مثال مطلب رو بهتر جا بندازیم. توی قطعه کد پایین، با استفاده از تابع سازنده لیست، دو لیست مجزا (و خالی) رو در دو متغییر x و y ذخیره کردیم. دو متغییر x و z دقیقا به ایک آبجکت اشاره می‌کنند.

چطور می‌تونیم بفهمیم دقیقاً به یک آبجکت اشاره می‌کنند؟ کافیه چک کنیم که هر متغییر دقیقاً به چه آدرسی از حافظه داره اشاره می‌کنه:

و اگر از عملگر is بپرسیم که کدوم دو متغییر هویت یکسانی دارند:

با توضیحات بالا شما باید الان یک همچین تصوری از این سه متغییر داشته باشید:

همون‌طور که گفتیم، محتوای هر سه متغییرها یک لیست خالی هست، در نتیجه بدیهی که انتظار داشته باشیم خروجی عملگر == برای سه متغییر یکی باشه:

  • نکته اول، در مواقعی که دو متغییر به یک آبجکت اشاره کنند، عملگر == خروجی is رو برای ما بر می‌گردونه.

نکته دوم، این هست که هر آبجکتی می‌تونه نحوه بررسی == رو خودش پیاده‌سازی کنه. این کار توسط تابع‌ __eq__ (بخونید داندِر equal) انجام میشه. مثلاً می‌تونیم آبجکتی بسازیم که با هر چیزی مقایسه بشه، همیشه True برگردونه.

  • اما برخلاف عملگر ==، هیچ روشی برای تغییر رفتار عملگر is وجود نداره! یعنی هیچ‌وقت عملگر is نمی‌تونه به ما دروغ بگه! همیشه به ما میگه که دو آبجکتی که دارن مقایسه می‌شن هویت‌شون (یک‌آدرس در حافظه) یکی هست یا نه.

دقیقا کی باید از is استفاده کرد؟

وقتی ابجکت‌هایی که در حال مقایسه هستند یکی از حالت‌های زیر باشند، به جای عملگر == باید از عملگر is استفاده کرد:

  • True (Example: if sunny_day is True)
  • False (Example: if normalize is False)
  • None (Example: if username is None)

چرا؟؟؟!

چون این آبجکت‌ها به صورت پیش‌فرض توی پایتون به صورت singleton پیاده‌سازی شده‌اند. یعنی مهم نیست چند تا از این‌ها داشته باشیم. همه‌ی اون‌ها به یک آدرس از حافظه اشاره می‌کند.

what does 'is' operator do?
what does 'is' operator do?

خیلی از چیزهای دیگه با هم برابر هستند اما به هیچ‌عنوان به یک آدرس از حافظه اشاره نمی‌کنند. پس فقط برای سه حالت بالا از عملگر is استفاده کنید.

Bad practice
Bad practice

جمع‌بندی

به طور کلی میشه گفت هر موقع به دنبال بررسی یکی بودن محتوای دو آبجکت هستیم، عملگری که باید استفاده بشه == هست. در مقابل، هر موقع انتظار میره که دو آبجکت هویتِ یکسان داشته باشند (به یک آدرس از حافظه اشاره کنند، یا دقیقا یکی هستند دو متغییر که صرفا به یک آدرس از حافظه اشاره می‌کنند) از عملگر is باید استفاده بشه.

x is something_unique_object

از اینجا کجا بریم؟

بهترین کار درست‌ کردن کد‌های قدیمی هست. هر کجا چیزی شبیه کدهای پایین دارید رو به حالت درستش بازنویسی کنید:

Bad patterns that should be fixed.
Bad patterns that should be fixed.

اگر مطلب به صورت کامل برای شما جا نیفتاده می‌تونید لینک‌های پایین رو بررسی کنید:

  • https://dbader.org/blog/difference-between-is-and-equals-in-python
  • https://hackernoon.com/demysti-py-vs-is-dbf51d76df08
pythonpythonicequalityidentitysingleton
Pythonista, Free Software Enthusiast. GNU/Linux Master. Network Security Researcher. Son. Brother.
شاید از این پست‌ها خوشتان بیاید